From 7597a309c282186e08f3a8723d6392431c40c1c4 Mon Sep 17 00:00:00 2001 From: Veado Date: Sat, 27 Aug 2022 18:19:48 +0200 Subject: [PATCH 1/2] Provide HDMode where needed (almost everywhere) Needed for MultiAccount feature (especially Ledger ETH) _CI will fail atm ..._ --- src/main/api/ledger/address.ts | 18 +-- src/main/api/ledger/binance/address.ts | 2 +- src/main/api/ledger/bitcoin/address.ts | 2 +- src/main/api/ledger/bitcoincash/address.ts | 2 +- src/main/api/ledger/cosmos/address.ts | 2 +- src/main/api/ledger/doge/address.ts | 2 +- src/main/api/ledger/ethereum/address.ts | 34 ++++-- src/main/api/ledger/ethereum/approve.ts | 7 +- src/main/api/ledger/ethereum/common.ts | 9 -- src/main/api/ledger/ethereum/transaction.ts | 16 +-- src/main/api/ledger/litecoin/address.ts | 2 +- src/main/api/ledger/thorchain/address.ts | 2 +- src/main/api/ledger/transaction.ts | 24 +++- .../components/deposit/add/SymDeposit.tsx | 10 +- .../components/settings/WalletSettings.tsx | 10 +- src/renderer/components/swap/Swap.stories.tsx | 6 +- src/renderer/components/swap/Swap.tsx | 6 +- .../components/wallet/assets/AssetDetails.tsx | 31 ++--- .../assets/AssetsTableCollapsable.stories.tsx | 41 ++++--- .../wallet/assets/AssetsTableCollapsable.tsx | 105 ++++++++-------- .../wallet/txs/send/SendFormBCH.stories.tsx | 1 + .../wallet/txs/send/SendFormBCH.tsx | 12 +- .../wallet/txs/send/SendFormBNB.stories.tsx | 1 + .../wallet/txs/send/SendFormBNB.tsx | 11 +- .../wallet/txs/send/SendFormBTC.stories.tsx | 1 + .../wallet/txs/send/SendFormBTC.tsx | 12 +- .../wallet/txs/send/SendFormCOSMOS.tsx | 9 +- .../wallet/txs/send/SendFormDOGE.stories.tsx | 1 + .../wallet/txs/send/SendFormDOGE.tsx | 12 +- .../wallet/txs/send/SendFormETH.stories.tsx | 9 +- .../wallet/txs/send/SendFormETH.tsx | 12 +- .../wallet/txs/send/SendFormLTC.stories.tsx | 1 + .../wallet/txs/send/SendFormLTC.tsx | 12 +- .../wallet/txs/send/SendFormTHOR.stories.tsx | 1 + .../wallet/txs/send/SendFormTHOR.tsx | 14 +-- .../wallet/txs/upgrade/Upgrade.stories.tsx | 1 + .../components/wallet/txs/upgrade/Upgrade.tsx | 6 +- src/renderer/contexts/EthereumContext.tsx | 8 +- src/renderer/helpers/fp/eq.ts | 47 ++++--- src/renderer/helpers/fp/ord.test.ts | 3 +- src/renderer/helpers/test/testWalletHelper.ts | 2 + src/renderer/hooks/useLedger.ts | 6 +- src/renderer/routes/wallet/wallet.test.ts | 115 +++--------------- src/renderer/routes/wallet/wallet.ts | 81 ++---------- src/renderer/services/binance/transaction.ts | 3 +- src/renderer/services/bitcoin/balances.ts | 17 ++- src/renderer/services/bitcoin/transaction.ts | 3 +- src/renderer/services/bitcoincash/balances.ts | 21 +++- .../services/bitcoincash/transaction.ts | 3 +- .../services/chain/transaction/common.ts | 11 +- .../services/chain/transaction/withdraw.ts | 5 +- src/renderer/services/chain/types.ts | 14 +-- src/renderer/services/clients/address.ts | 3 +- src/renderer/services/clients/balances.ts | 28 ++++- src/renderer/services/cosmos/balances.ts | 13 +- src/renderer/services/cosmos/transaction.ts | 3 +- src/renderer/services/doge/balances.ts | 14 ++- src/renderer/services/doge/transaction.ts | 3 +- src/renderer/services/ethereum/balances.ts | 16 ++- src/renderer/services/ethereum/transaction.ts | 23 +++- src/renderer/services/ethereum/types.ts | 4 +- src/renderer/services/litecoin/balances.ts | 14 ++- src/renderer/services/litecoin/transaction.ts | 3 +- src/renderer/services/thorchain/balances.ts | 13 +- .../services/thorchain/transaction.ts | 6 +- src/renderer/services/wallet/balances.ts | 97 +++++++++++---- src/renderer/services/wallet/common.ts | 13 +- src/renderer/services/wallet/ledger.ts | 13 +- src/renderer/services/wallet/transaction.ts | 2 +- src/renderer/services/wallet/types.ts | 34 ++++-- src/renderer/services/wallet/util.test.ts | 48 +++++--- src/renderer/services/wallet/util.ts | 25 ++-- .../views/wallet/AssetDetailsView.tsx | 103 ++++------------ src/renderer/views/wallet/AssetsView.tsx | 41 +++---- .../views/wallet/WalletSettingsView.tsx | 6 +- src/renderer/views/wallet/send/SendView.tsx | 102 ++++------------ .../views/wallet/send/SendViewBCH.tsx | 17 +-- .../views/wallet/send/SendViewBNB.tsx | 22 ++-- .../views/wallet/send/SendViewBTC.tsx | 17 +-- .../views/wallet/send/SendViewCOSMOS.tsx | 16 +-- .../views/wallet/send/SendViewDOGE.tsx | 17 +-- .../views/wallet/send/SendViewETH.tsx | 24 ++-- .../views/wallet/send/SendViewLTC.tsx | 15 +-- .../views/wallet/send/SendViewTHOR.tsx | 16 +-- .../views/wallet/upgrade/UpgradeView.tsx | 25 +--- src/renderer/views/wallet/upgrade/types.ts | 3 +- src/shared/api/io.test.ts | 22 ++-- src/shared/api/io.ts | 33 +++-- src/shared/api/types.ts | 8 +- src/shared/ethereum/const.ts | 4 +- src/shared/ethereum/ledger.ts | 6 +- src/shared/ethereum/types.ts | 2 +- src/shared/mock/api.ts | 2 +- src/shared/mock/wallet.ts | 18 ++- src/shared/utils/guard.test.ts | 8 +- src/shared/utils/guard.ts | 10 +- src/shared/wallet/types.ts | 6 +- 97 files changed, 836 insertions(+), 868 deletions(-) delete mode 100644 src/main/api/ledger/ethereum/common.ts diff --git a/src/main/api/ledger/address.ts b/src/main/api/ledger/address.ts index 94eb59473..5c8bef24d 100644 --- a/src/main/api/ledger/address.ts +++ b/src/main/api/ledger/address.ts @@ -12,7 +12,7 @@ import { import * as E from 'fp-ts/Either' import { IPCLedgerAdddressParams, LedgerError, LedgerErrorId } from '../../../shared/api/types' -import { isError } from '../../../shared/utils/guard' +import { isError, isEthHDMode } from '../../../shared/utils/guard' import { WalletAddress } from '../../../shared/wallet/types' import { getAddress as getBNBAddress, verifyAddress as verifyBNBAddress } from './binance/address' import { getAddress as getBTCAddress, verifyAddress as verifyBTCAddress } from './bitcoin/address' @@ -27,7 +27,7 @@ export const getAddress = async ({ chain, network, walletIndex, - ethDerivationMode + hdMode }: IPCLedgerAdddressParams): Promise> => { try { let res: E.Either @@ -52,13 +52,13 @@ export const getAddress = async ({ res = await getDOGEAddress(transport, network, walletIndex) break case ETHChain: { - if (!ethDerivationMode) { + if (!isEthHDMode(hdMode)) { res = E.left({ errorId: LedgerErrorId.INVALID_ETH_DERIVATION_MODE, - msg: `'ethDerivationMode' is needed for ETH to get Ledger address` + msg: `Invaid 'EthHDMode' - needed for ETH to get Ledger address` }) } else { - res = await getETHAddress(transport, walletIndex, ethDerivationMode) + res = await getETHAddress({ transport, walletIndex, ethHdMode: hdMode }) } break } @@ -81,7 +81,7 @@ export const getAddress = async ({ } } -export const verifyLedgerAddress = async ({ chain, network, walletIndex }: IPCLedgerAdddressParams) => { +export const verifyLedgerAddress = async ({ chain, network, walletIndex, hdMode }: IPCLedgerAdddressParams) => { const transport = await TransportNodeHidSingleton.open() let result = false switch (chain) { @@ -103,9 +103,11 @@ export const verifyLedgerAddress = async ({ chain, network, walletIndex }: IPCLe case DOGEChain: result = await verifyDOGEAddress({ transport, network, walletIndex }) break - case ETHChain: - result = await verifyETHAddress(transport, walletIndex) + case ETHChain: { + if (!isEthHDMode(hdMode)) throw Error(`Invaid 'EthHDMode' - needed for ETH to verify Ledger address`) + result = await verifyETHAddress({ transport, walletIndex, ethHdMode: hdMode }) break + } case CosmosChain: result = await verifyCOSMOSAddress(transport, walletIndex) break diff --git a/src/main/api/ledger/binance/address.ts b/src/main/api/ledger/binance/address.ts index 28a989ea0..1c9bc58a4 100644 --- a/src/main/api/ledger/binance/address.ts +++ b/src/main/api/ledger/binance/address.ts @@ -25,7 +25,7 @@ export const getAddress = async ( if (pk) { // get address from pubkey const address = crypto.getAddressFromPublicKey(pk.toString('hex'), prefix) - return E.right({ address, chain: BNBChain, type: 'ledger', walletIndex }) + return E.right({ address, chain: BNBChain, type: 'ledger', walletIndex, hdMode: 'default' }) } else { return E.left({ errorId: LedgerErrorId.INVALID_PUBKEY, diff --git a/src/main/api/ledger/bitcoin/address.ts b/src/main/api/ledger/bitcoin/address.ts index 093e0bc25..227170625 100644 --- a/src/main/api/ledger/bitcoin/address.ts +++ b/src/main/api/ledger/bitcoin/address.ts @@ -22,7 +22,7 @@ export const getAddress = async ( const { bitcoinAddress } = await app.getWalletPublicKey(derivePath, { format: 'bech32' // bech32 format with 84' paths }) - return E.right({ address: bitcoinAddress, chain: BTCChain, type: 'ledger', walletIndex }) + return E.right({ address: bitcoinAddress, chain: BTCChain, type: 'ledger', walletIndex, hdMode: 'default' }) } catch (error) { return E.left({ errorId: LedgerErrorId.GET_ADDRESS_FAILED, diff --git a/src/main/api/ledger/bitcoincash/address.ts b/src/main/api/ledger/bitcoincash/address.ts index 28706c836..fc758a81c 100644 --- a/src/main/api/ledger/bitcoincash/address.ts +++ b/src/main/api/ledger/bitcoincash/address.ts @@ -24,7 +24,7 @@ export const getAddress = async ( // @see https://github.com/LedgerHQ/ledgerjs/blob/master/packages/hw-app-btc/README.md#parameters-2 format: 'cashaddr' }) - return E.right({ address: bchAddress, chain: BCHChain, type: 'ledger', walletIndex }) + return E.right({ address: bchAddress, chain: BCHChain, type: 'ledger', walletIndex, hdMode: 'default' }) } catch (error) { return E.left({ errorId: LedgerErrorId.GET_ADDRESS_FAILED, diff --git a/src/main/api/ledger/cosmos/address.ts b/src/main/api/ledger/cosmos/address.ts index d0c6744ee..a772f0cec 100644 --- a/src/main/api/ledger/cosmos/address.ts +++ b/src/main/api/ledger/cosmos/address.ts @@ -24,7 +24,7 @@ export const getAddress = async ( msg: `Getting 'address' from Ledger's Cosmos app failed` }) } - return E.right({ address, chain: CosmosChain, type: 'ledger', walletIndex }) + return E.right({ address, chain: CosmosChain, type: 'ledger', walletIndex, hdMode: 'default' }) } catch (error) { return E.left({ errorId: LedgerErrorId.GET_ADDRESS_FAILED, diff --git a/src/main/api/ledger/doge/address.ts b/src/main/api/ledger/doge/address.ts index 218a842c7..4d1970185 100644 --- a/src/main/api/ledger/doge/address.ts +++ b/src/main/api/ledger/doge/address.ts @@ -24,7 +24,7 @@ export const getAddress = async ( // @see https://github.com/LedgerHQ/ledgerjs/blob/master/packages/hw-app-btc/README.md#parameters-2 format: 'legacy' }) - return E.right({ address, chain: DOGEChain, type: 'ledger', walletIndex }) + return E.right({ address, chain: DOGEChain, type: 'ledger', walletIndex, hdMode: 'default' }) } catch (error) { return E.left({ errorId: LedgerErrorId.GET_ADDRESS_FAILED, diff --git a/src/main/api/ledger/ethereum/address.ts b/src/main/api/ledger/ethereum/address.ts index b452e640e..dc1be8412 100644 --- a/src/main/api/ledger/ethereum/address.ts +++ b/src/main/api/ledger/ethereum/address.ts @@ -5,23 +5,26 @@ import * as E from 'fp-ts/Either' import { LedgerError, LedgerErrorId } from '../../../../shared/api/types' import { getDerivationPath } from '../../../../shared/ethereum/ledger' -import { EthDerivationMode } from '../../../../shared/ethereum/types' +import { EthHDMode } from '../../../../shared/ethereum/types' import { isError } from '../../../../shared/utils/guard' import { WalletAddress } from '../../../../shared/wallet/types' -import { getDerivationMode } from './common' -export const getAddress = async ( - transport: Transport, - walletIndex: number, - mode: EthDerivationMode -): Promise> => { +export const getAddress = async ({ + transport, + walletIndex, + ethHdMode +}: { + transport: Transport + walletIndex: number + ethHdMode: EthHDMode +}): Promise> => { try { const app = new EthApp(transport) - const path = getDerivationPath(walletIndex, mode) + const path = getDerivationPath(walletIndex, ethHdMode) const { address } = await app.getAddress(path) if (address) { - return E.right({ address, chain: ETHChain, type: 'ledger', walletIndex }) + return E.right({ address, chain: ETHChain, type: 'ledger', walletIndex, hdMode: ethHdMode }) } else { return E.left({ errorId: LedgerErrorId.INVALID_PUBKEY, @@ -36,10 +39,17 @@ export const getAddress = async ( } } -export const verifyAddress = async (transport: Transport, walletIndex: number) => { +export const verifyAddress = async ({ + transport, + walletIndex, + ethHdMode +}: { + transport: Transport + walletIndex: number + ethHdMode: EthHDMode +}) => { const app = new EthApp(transport) - const mode = await getDerivationMode() - const path = getDerivationPath(walletIndex, mode) + const path = getDerivationPath(walletIndex, ethHdMode) const _ = await app.getAddress(path, true) return true } diff --git a/src/main/api/ledger/ethereum/approve.ts b/src/main/api/ledger/ethereum/approve.ts index 2547099ef..73babe578 100644 --- a/src/main/api/ledger/ethereum/approve.ts +++ b/src/main/api/ledger/ethereum/approve.ts @@ -10,14 +10,14 @@ import { IPCLedgerApproveERC20TokenParams } from '../../../../shared/api/io' import { DEFAULT_APPROVE_GAS_LIMIT_FALLBACK, FEE_BOUNDS } from '../../../../shared/ethereum/const' import { getDerivationPath } from '../../../../shared/ethereum/ledger' import { toClientNetwork } from '../../../../shared/utils/client' -import { getDerivationMode } from './common' import { LedgerSigner } from './LedgerSigner' export const approveLedgerERC20Token = async ({ network, contractAddress, spenderAddress, - walletIndex + walletIndex, + ethHdMode }: IPCLedgerApproveERC20TokenParams): Promise => { const { ethplorerApiKey, ethplorerUrl } = getEthplorerCreds() @@ -35,8 +35,7 @@ export const approveLedgerERC20Token = async ({ const transport = await TransportNodeHidSingleton.open() const app = new EthApp(transport) - const mode = await getDerivationMode() - const path = getDerivationPath(walletIndex, mode) + const path = getDerivationPath(walletIndex, ethHdMode) const provider = client.getProvider() const signer = new LedgerSigner({ provider, path, app }) diff --git a/src/main/api/ledger/ethereum/common.ts b/src/main/api/ledger/ethereum/common.ts deleted file mode 100644 index 3ea763c1c..000000000 --- a/src/main/api/ledger/ethereum/common.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { DEFAULT_STORAGES } from '../../../../shared/const' -import { EthDerivationMode } from '../../../../shared/ethereum/types' -import { getFileContent as getFileStore } from '../../fileStore' - -export const getDerivationMode = async (): Promise => { - // get `EthDerivationMode` from file storage - const { ethDerivationMode } = await getFileStore('common', DEFAULT_STORAGES.common) - return ethDerivationMode -} diff --git a/src/main/api/ledger/ethereum/transaction.ts b/src/main/api/ledger/ethereum/transaction.ts index ef6eeaec8..6ae2fbcfe 100644 --- a/src/main/api/ledger/ethereum/transaction.ts +++ b/src/main/api/ledger/ethereum/transaction.ts @@ -12,9 +12,9 @@ import { getInfuraCreds } from '../../../../shared/api/infura' import { LedgerError, LedgerErrorId, Network } from '../../../../shared/api/types' import { FEE_BOUNDS } from '../../../../shared/ethereum/const' import { getDerivationPath } from '../../../../shared/ethereum/ledger' +import { EthHDMode } from '../../../../shared/ethereum/types' import { toClientNetwork } from '../../../../shared/utils/client' import { isError } from '../../../../shared/utils/guard' -import { getDerivationMode } from './common' import { LedgerSigner } from './LedgerSigner' /** @@ -28,7 +28,8 @@ export const send = async ({ memo, recipient, feeOption, - walletIndex + walletIndex, + ethHDMode }: { asset: Asset transport: Transport @@ -38,6 +39,7 @@ export const send = async ({ memo?: string feeOption: FeeOption walletIndex: number + ethHDMode: EthHDMode }): Promise> => { try { const { ethplorerApiKey, ethplorerUrl } = getEthplorerCreds() @@ -56,8 +58,7 @@ export const send = async ({ }) const app = new EthApp(transport) - const mode = await getDerivationMode() - const path = getDerivationPath(walletIndex, mode) + const path = getDerivationPath(walletIndex, ethHDMode) const provider = client.getProvider() const signer = new LedgerSigner({ provider, path, app }) @@ -98,7 +99,8 @@ export const deposit = async ({ memo, recipient, walletIndex, - feeOption + feeOption, + ethHDMode }: { asset: Asset router: Address @@ -109,6 +111,7 @@ export const deposit = async ({ memo?: string walletIndex: number feeOption: FeeOption + ethHDMode: EthHDMode }): Promise> => { try { const address = ETH.getAssetAddress(asset) @@ -138,8 +141,7 @@ export const deposit = async ({ }) const app = new EthApp(transport) - const mode = await getDerivationMode() - const path = getDerivationPath(walletIndex, mode) + const path = getDerivationPath(walletIndex, ethHDMode) const provider = client.getProvider() const signer = new LedgerSigner({ provider, path, app }) diff --git a/src/main/api/ledger/litecoin/address.ts b/src/main/api/ledger/litecoin/address.ts index 19135565c..ec2c9ded4 100644 --- a/src/main/api/ledger/litecoin/address.ts +++ b/src/main/api/ledger/litecoin/address.ts @@ -22,7 +22,7 @@ export const getAddress = async ( const { bitcoinAddress: ltcAddress } = await app.getWalletPublicKey(derivePath, { format: 'bech32' // bech32 format with 84' paths }) - return E.right({ address: ltcAddress, chain: LTCChain, type: 'ledger', walletIndex }) + return E.right({ address: ltcAddress, chain: LTCChain, type: 'ledger', walletIndex, hdMode: 'default' }) } catch (error) { return E.left({ errorId: LedgerErrorId.GET_ADDRESS_FAILED, diff --git a/src/main/api/ledger/thorchain/address.ts b/src/main/api/ledger/thorchain/address.ts index 24d01e1ed..8b36da845 100644 --- a/src/main/api/ledger/thorchain/address.ts +++ b/src/main/api/ledger/thorchain/address.ts @@ -28,7 +28,7 @@ export const getAddress = async ( msg: `Getting 'bech32Address' from Ledger's THORChain App failed` }) } - return E.right({ address: bech32Address, chain: THORChain, type: 'ledger', walletIndex }) + return E.right({ address: bech32Address, chain: THORChain, type: 'ledger', walletIndex, hdMode: 'default' }) } catch (error) { return E.left({ errorId: LedgerErrorId.GET_ADDRESS_FAILED, diff --git a/src/main/api/ledger/transaction.ts b/src/main/api/ledger/transaction.ts index d5a9a7c63..3381f2a84 100644 --- a/src/main/api/ledger/transaction.ts +++ b/src/main/api/ledger/transaction.ts @@ -15,7 +15,7 @@ import * as E from 'fp-ts/Either' import { IPCLedgerDepositTxParams, IPCLedgerSendTxParams } from '../../../shared/api/io' import { LedgerError, LedgerErrorId } from '../../../shared/api/types' -import { isError } from '../../../shared/utils/guard' +import { isError, isEthHDMode } from '../../../shared/utils/guard' import * as BNB from './binance/transaction' import * as BTC from './bitcoin/transaction' import * as BCH from './bitcoincash/transaction' @@ -37,7 +37,8 @@ export const sendTx = async ({ feeRate, feeOption, walletIndex, - nodeUrl + nodeUrl, + hdMode }: IPCLedgerSendTxParams): Promise> => { try { const transport = await TransportNodeHidSingleton.open() @@ -123,6 +124,11 @@ export const sendTx = async ({ errorId: LedgerErrorId.INVALID_DATA, msg: `Fee option needs to be set to send Ledger transaction on ${chainToString(chain)}` }) + } else if (!isEthHDMode(hdMode)) { + res = E.left({ + errorId: LedgerErrorId.INVALID_DATA, + msg: `Invalid EthHDMode set - needed to send Ledger transaction on ${chainToString(chain)}` + }) } else { res = await ETH.send({ asset, @@ -132,7 +138,8 @@ export const sendTx = async ({ amount, memo, walletIndex, - feeOption + feeOption, + ethHDMode: hdMode }) } break @@ -186,7 +193,8 @@ export const deposit = async ({ memo, walletIndex, feeOption, - nodeUrl + nodeUrl, + hdMode }: IPCLedgerDepositTxParams): Promise> => { try { const transport = await TransportNodeHidSingleton.open() @@ -223,6 +231,11 @@ export const deposit = async ({ errorId: LedgerErrorId.INVALID_DATA, msg: `Fee option needs to be defined to send Ledger transaction on ${chainToString(chain)}` }) + } else if (!isEthHDMode(hdMode)) { + res = E.left({ + errorId: LedgerErrorId.INVALID_DATA, + msg: `Invalid EthHDMode set - needed to send Ledger transaction on ${chainToString(chain)}` + }) } else { res = await ETH.deposit({ asset, @@ -233,7 +246,8 @@ export const deposit = async ({ memo, walletIndex, recipient, - feeOption + feeOption, + ethHDMode: hdMode }) } break diff --git a/src/renderer/components/deposit/add/SymDeposit.tsx b/src/renderer/components/deposit/add/SymDeposit.tsx index 1444e1ca9..70898adf4 100644 --- a/src/renderer/components/deposit/add/SymDeposit.tsx +++ b/src/renderer/components/deposit/add/SymDeposit.tsx @@ -406,13 +406,14 @@ export const SymDeposit: React.FC = (props) => { return FP.pipe( sequenceTOption(oNeedApprovement, oTokenAddress, oRouterAddress, oAssetWB), - O.map(([_, tokenAddress, routerAddress, { walletAddress, walletIndex, walletType }]) => ({ + O.map(([_, tokenAddress, routerAddress, { walletAddress, walletIndex, walletType, hdMode }]) => ({ network, spenderAddress: routerAddress, contractAddress: tokenAddress, fromAddress: walletAddress, walletIndex, - walletType + walletType, + hdMode })) ) }, [oPoolAddress, asset, needApprovement, oAssetWB, network]) @@ -1085,7 +1086,7 @@ export const SymDeposit: React.FC = (props) => { const submitApproveTx = useCallback(() => { FP.pipe( oApproveParams, - O.map(({ walletIndex, walletType, contractAddress, spenderAddress, fromAddress }) => + O.map(({ walletIndex, walletType, contractAddress, spenderAddress, fromAddress, hdMode }) => subscribeApproveState( approveERC20Token$({ network, @@ -1093,7 +1094,8 @@ export const SymDeposit: React.FC = (props) => { spenderAddress, fromAddress, walletIndex, - walletType + walletType, + hdMode }) ) ) diff --git a/src/renderer/components/settings/WalletSettings.tsx b/src/renderer/components/settings/WalletSettings.tsx index c5de95699..862675644 100644 --- a/src/renderer/components/settings/WalletSettings.tsx +++ b/src/renderer/components/settings/WalletSettings.tsx @@ -27,7 +27,7 @@ import { useNavigate } from 'react-router-dom' import { KeystoreId, Network } from '../../../shared/api/types' import { getDerivationPath as getEthDerivationPath } from '../../../shared/ethereum/ledger' -import { EthDerivationMode } from '../../../shared/ethereum/types' +import { EthHDMode } from '../../../shared/ethereum/types' import { isError } from '../../../shared/utils/guard' import { WalletAddress } from '../../../shared/wallet/types' import { ReactComponent as UnlockOutlined } from '../../assets/svg/icon-unlock-warning.svg' @@ -81,12 +81,12 @@ type Props = { addLedgerAddress$: (params: { chain: Chain walletIndex: number - ethDerivationMode: O.Option + ethDerivationMode: O.Option }) => LedgerAddressLD verifyLedgerAddress$: (params: { chain: Chain walletIndex: number - ethDerivationMode: O.Option + ethDerivationMode: O.Option }) => VerifiedLedgerAddressLD removeLedgerAddress: (chain: Chain) => void keystoreUnlocked: KeystoreUnlocked @@ -194,7 +194,7 @@ export const WalletSettings: React.FC = (props): JSX.Element => { const [ledgerAddressToVerify, setLedgerAddressToVerify] = useState(O.none) - const [ethDerivationMode, setEthDerivationMode] = useState('ledgerlive') + const [ethDerivationMode, setEthDerivationMode] = useState('ledgerlive') const renderLedgerNotSupported = useMemo( () => ( @@ -251,7 +251,7 @@ export const WalletSettings: React.FC = (props): JSX.Element => { (chain: Chain, oAddress: O.Option) => { const renderAddAddress = () => { const onChangeEthDerivationMode = (e: RadioChangeEvent) => { - setEthDerivationMode(e.target.value as EthDerivationMode) + setEthDerivationMode(e.target.value as EthHDMode) } const selectedWalletIndex = walletIndexMap[chain] diff --git a/src/renderer/components/swap/Swap.stories.tsx b/src/renderer/components/swap/Swap.stories.tsx index 40862173f..e3f4528fb 100644 --- a/src/renderer/components/swap/Swap.stories.tsx +++ b/src/renderer/components/swap/Swap.stories.tsx @@ -70,14 +70,16 @@ const defaultProps: SwapProps = { amount: assetToBase(assetAmount(100)), walletType: 'keystore', walletIndex: 0, - walletAddress: 'wallet-address-rune' + walletAddress: 'wallet-address-rune', + hdMode: 'default' }, { asset: AssetBTC, walletIndex: 0, amount: assetToBase(assetAmount(1)), walletType: 'keystore', - walletAddress: 'wallet-address-btc' + walletAddress: 'wallet-address-btc', + hdMode: 'default' } ]), loading: false diff --git a/src/renderer/components/swap/Swap.tsx b/src/renderer/components/swap/Swap.tsx index 493d35f0a..9287f2a2d 100644 --- a/src/renderer/components/swap/Swap.tsx +++ b/src/renderer/components/swap/Swap.tsx @@ -534,12 +534,13 @@ export const Swap = ({ return FP.pipe( sequenceTOption(oNeedApprovement, oTokenAddress, oRouterAddress, oSourceAssetWB), - O.map(([_, tokenAddress, routerAddress, { walletAddress, walletIndex, walletType }]) => ({ + O.map(([_, tokenAddress, routerAddress, { walletAddress, walletIndex, walletType, hdMode }]) => ({ network, spenderAddress: routerAddress, contractAddress: tokenAddress, fromAddress: walletAddress, walletIndex, + hdMode, walletType })) ) @@ -837,7 +838,7 @@ export const Swap = ({ const submitApproveTx = useCallback(() => { FP.pipe( oApproveParams, - O.map(({ walletIndex, walletType, contractAddress, spenderAddress, fromAddress }) => + O.map(({ walletIndex, walletType, hdMode, contractAddress, spenderAddress, fromAddress }) => subscribeApproveState( approveERC20Token$({ network, @@ -845,6 +846,7 @@ export const Swap = ({ spenderAddress, fromAddress, walletIndex, + hdMode, walletType }) ) diff --git a/src/renderer/components/wallet/assets/AssetDetails.tsx b/src/renderer/components/wallet/assets/AssetDetails.tsx index f312a4ef2..034d25248 100644 --- a/src/renderer/components/wallet/assets/AssetDetails.tsx +++ b/src/renderer/components/wallet/assets/AssetDetails.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useMemo, useState } from 'react' import { Address } from '@xchainjs/xchain-client' -import { Asset, AssetAmount, assetToBase, assetToString, BaseAmount } from '@xchainjs/xchain-util' +import { Asset, AssetAmount, assetToBase, BaseAmount } from '@xchainjs/xchain-util' import { Row, Col, Grid } from 'antd' import * as FP from 'fp-ts/lib/function' import * as O from 'fp-ts/lib/Option' @@ -26,7 +26,6 @@ import * as Styled from './AssetDetails.styles' type Props = { walletType: WalletType - walletIndex: number txsPageRD: TxsPageRD balances: O.Option asset: Asset @@ -43,7 +42,6 @@ type Props = { export const AssetDetails: React.FC = (props): JSX.Element => { const { walletType, - walletIndex, txsPageRD, balances: oBalances, asset, @@ -64,24 +62,15 @@ export const AssetDetails: React.FC = (props): JSX.Element => { const intl = useIntl() const walletActionSendClick = useCallback(() => { - const routeParams = { - asset: assetToString(asset), - walletAddress, - walletType, - walletIndex: walletIndex.toString() - } - navigate(walletRoutes.send.path(routeParams)) - }, [asset, navigate, walletAddress, walletIndex, walletType]) + navigate(walletRoutes.send.path()) + }, [navigate]) const walletActionDepositClick = useCallback(() => { const path = walletRoutes.interact.path({ - interactType: 'bond', - walletType, - walletAddress, - walletIndex: walletIndex.toString() + interactType: 'bond' }) navigate(path) - }, [walletType, walletAddress, walletIndex, navigate]) + }, [navigate]) const isNonNativeRuneAsset: boolean = useMemo( () => AssetHelper.isNonNativeRuneAsset(asset, network), @@ -89,15 +78,9 @@ export const AssetDetails: React.FC = (props): JSX.Element => { ) const walletActionUpgradeNonNativeRuneClick = useCallback(() => { - const path = walletRoutes.upgradeRune.path({ - asset: assetToString(asset), - walletAddress, - network, - walletType, - walletIndex: walletIndex.toString() - }) + const path = walletRoutes.upgradeRune.path() navigate(path) - }, [asset, walletAddress, network, walletType, walletIndex, navigate]) + }, [navigate]) const refreshHandler = useCallback(() => { loadTxsHandler({ limit: MAX_ITEMS_PER_PAGE, offset: (currentPage - 1) * MAX_ITEMS_PER_PAGE }) diff --git a/src/renderer/components/wallet/assets/AssetsTableCollapsable.stories.tsx b/src/renderer/components/wallet/assets/AssetsTableCollapsable.stories.tsx index f5909122a..006929298 100644 --- a/src/renderer/components/wallet/assets/AssetsTableCollapsable.stories.tsx +++ b/src/renderer/components/wallet/assets/AssetsTableCollapsable.stories.tsx @@ -1,8 +1,6 @@ import * as RD from '@devexperts/remote-data-ts' import { ComponentMeta } from '@storybook/react' -import { Address } from '@xchainjs/xchain-client' import { - Asset, AssetBNB, AssetBTC, AssetLTC, @@ -26,20 +24,15 @@ import { getMockRDValueFactory, RDStatus } from '../../../../shared/mock/rdBySta import { WalletType } from '../../../../shared/wallet/types' import { RUNE_PRICE_POOL } from '../../../helpers/poolHelper' import { WalletBalances } from '../../../services/clients' -import { ApiError, ChainBalances, ErrorId } from '../../../services/wallet/types' +import { ApiError, ChainBalances, ErrorId, SelectedWalletAsset } from '../../../services/wallet/types' import { AssetsTableCollapsable } from './index' const apiError: ApiError = { errorId: ErrorId.GET_BALANCES, msg: 'error message' } -const selectAssetHandler = ({ - asset, - walletType, - walletAddress -}: { - asset: Asset - walletAddress: Address - walletType: WalletType -}) => console.log('selectAssetHandler params ', assetToString(asset), walletType, walletAddress) +const selectAssetHandler = ({ asset, walletType, walletAddress }: SelectedWalletAsset) => + console.log('selectAssetHandler params ', assetToString(asset), walletType, walletAddress) +const upgradeAssetHandler = ({ asset, walletType, walletAddress }: SelectedWalletAsset) => + console.log('upgradeAssetHandler params ', assetToString(asset), walletType, walletAddress) const balances: Partial> = { [BNBChain]: [ @@ -48,20 +41,23 @@ const balances: Partial> = { walletAddress: O.some('bnb keystore'), walletIndex: 0, chain: BNBChain, + hdMode: 'default', balances: RD.success([ { walletType: 'keystore', amount: baseAmount('1000000'), asset: AssetBNB, walletAddress: 'BNB wallet address', - walletIndex: 0 + walletIndex: 0, + hdMode: 'default' }, { walletType: 'keystore', amount: baseAmount('300000000'), asset: AssetRune67C, walletAddress: 'BNB wallet address', - walletIndex: 0 + walletIndex: 0, + hdMode: 'default' } ]), balancesType: 'all' @@ -73,13 +69,15 @@ const balances: Partial> = { walletAddress: O.some('btc keystore'), walletIndex: 0, chain: BTCChain, + hdMode: 'default', balances: RD.success([ { walletType: 'keystore', amount: baseAmount('1000000'), asset: AssetBTC, walletAddress: 'BNB wallet address', - walletIndex: 0 + walletIndex: 0, + hdMode: 'default' } ]), balancesType: 'all' @@ -90,6 +88,7 @@ const balances: Partial> = { walletType: 'keystore', walletAddress: O.some('eth keystore'), walletIndex: 0, + hdMode: 'default', chain: ETHChain, balances: RD.success([ { @@ -97,7 +96,8 @@ const balances: Partial> = { amount: baseAmount('300000000'), asset: AssetETH, walletAddress: 'ETH wallet address', - walletIndex: 0 + walletIndex: 0, + hdMode: 'default' } ]), balancesType: 'all' @@ -108,6 +108,7 @@ const balances: Partial> = { walletType: 'keystore', walletAddress: O.some('thor keystore'), walletIndex: 0, + hdMode: 'default', chain: THORChain, balances: RD.success([ { @@ -115,7 +116,8 @@ const balances: Partial> = { amount: baseAmount('1000000'), asset: AssetRuneNative, walletAddress: 'BNB wallet address', - walletIndex: 0 + walletIndex: 0, + hdMode: 'default' } ]), balancesType: 'all' @@ -126,6 +128,7 @@ const balances: Partial> = { walletType: 'keystore', walletAddress: O.some('ltc keystore'), walletIndex: 0, + hdMode: 'default', chain: LTCChain, balances: RD.success([ { @@ -133,7 +136,8 @@ const balances: Partial> = { amount: baseAmount('1000000'), asset: AssetLTC, walletAddress: 'LTC wallet address', - walletIndex: 0 + walletIndex: 0, + hdMode: 'default' } ]), balancesType: 'all' @@ -167,6 +171,7 @@ const Template = (args: Partial>) => { return ( diff --git a/src/renderer/components/wallet/assets/AssetsTableCollapsable.tsx b/src/renderer/components/wallet/assets/AssetsTableCollapsable.tsx index 724d43a24..0c5f43dca 100644 --- a/src/renderer/components/wallet/assets/AssetsTableCollapsable.tsx +++ b/src/renderer/components/wallet/assets/AssetsTableCollapsable.tsx @@ -2,14 +2,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import * as RD from '@devexperts/remote-data-ts' import { Address, Balance } from '@xchainjs/xchain-client' -import { - Asset, - assetToString, - baseToAsset, - chainToString, - formatAssetAmountCurrency, - isSynthAsset -} from '@xchainjs/xchain-util' +import { Asset, baseToAsset, chainToString, formatAssetAmountCurrency, isSynthAsset } from '@xchainjs/xchain-util' import { Col, Collapse, Grid, Row } from 'antd' import { ScreenMap } from 'antd/lib/_util/responsiveObserve' import { ColumnType } from 'antd/lib/table' @@ -17,19 +10,24 @@ import * as A from 'fp-ts/lib/Array' import * as FP from 'fp-ts/lib/function' import * as O from 'fp-ts/lib/Option' import { useIntl } from 'react-intl' -import { useNavigate } from 'react-router-dom' import { Network } from '../../../../shared/api/types' import { isKeystoreWallet } from '../../../../shared/utils/guard' -import { WalletType } from '../../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../../shared/wallet/types' import { disableRuneUpgrade, isNonNativeRuneAsset, isUSDAsset } from '../../../helpers/assetHelper' import { getChainAsset } from '../../../helpers/chainHelper' import { getPoolPriceValue } from '../../../helpers/poolHelper' -import * as walletRoutes from '../../../routes/wallet' import { WalletBalancesRD } from '../../../services/clients' import { PoolDetails } from '../../../services/midgard/types' import { MimirHaltRD } from '../../../services/thorchain/types' -import { ApiError, ChainBalance, ChainBalances, WalletBalance, WalletBalances } from '../../../services/wallet/types' +import { + ApiError, + ChainBalance, + ChainBalances, + SelectedWalletAsset, + WalletBalance, + WalletBalances +} from '../../../services/wallet/types' import { walletTypeToI18n } from '../../../services/wallet/util' import { PricePool } from '../../../views/pools/Pools.types' import { ErrorView } from '../../shared/error/' @@ -44,18 +42,8 @@ type Props = { chainBalances: ChainBalances pricePool: PricePool poolDetails: PoolDetails - selectAssetHandler?: ({ - asset, - walletAddress, - walletType, - walletIndex - }: { - asset: Asset - walletAddress: Address - walletType: WalletType - walletIndex: number - }) => void - setSelectedAsset?: (oAsset: O.Option) => void + selectAssetHandler: (asset: SelectedWalletAsset) => void + upgradeAssetHandler: (asset: SelectedWalletAsset) => void network: Network mimirHalt: MimirHaltRD } @@ -65,14 +53,13 @@ export const AssetsTableCollapsable: React.FC = (props): JSX.Element => { chainBalances = [], pricePool, poolDetails, - selectAssetHandler = (_) => {}, - setSelectedAsset = () => {}, + selectAssetHandler, + upgradeAssetHandler, mimirHalt: mimirHaltRD, network } = props const intl = useIntl() - const navigate = useNavigate() const screenMap: ScreenMap = Grid.useBreakpoint() const [showQRModal, setShowQRModal] = useState>(O.none) @@ -96,12 +83,22 @@ export const AssetsTableCollapsable: React.FC = (props): JSX.Element => { ) const onRowHandler = useCallback( - (oWalletAddress: O.Option
, walletType: WalletType, walletIndex: number) => + ({ + oWalletAddress, + walletType, + walletIndex, + hdMode + }: { + oWalletAddress: O.Option
+ walletType: WalletType + walletIndex: number + hdMode: HDMode + }) => ({ asset }: Balance) => { // Disable click for NativeRUNE if Thorchain is halted const onClick = FP.pipe( oWalletAddress, - O.map((walletAddress) => () => selectAssetHandler({ asset, walletAddress, walletType, walletIndex })), + O.map((walletAddress) => () => selectAssetHandler({ asset, walletAddress, walletType, walletIndex, hdMode })), // TODO(@Veado) Add error message / alert O.getOrElse(() => () => console.error('Unknown address')) ) @@ -135,23 +132,14 @@ export const AssetsTableCollapsable: React.FC = (props): JSX.Element => { const tickerColumn: ColumnType = useMemo( () => ({ width: 80, - render: ({ asset, walletAddress, walletType, walletIndex }: WalletBalance) => { + render: ({ asset, walletAddress, walletType, walletIndex, hdMode }: WalletBalance) => { // Disable UPGRADE button if needed const disableUpgradeButton = disableRuneUpgrade({ asset, haltThorChain, haltEthChain, haltBnbChain, network }) const onClickUpgradeButtonHandler = (e: React.MouseEvent) => { e.preventDefault() e.stopPropagation() - setSelectedAsset(O.some(asset)) - navigate( - walletRoutes.upgradeRune.path({ - asset: assetToString(asset), - walletAddress, - network, - walletType, - walletIndex: walletIndex ? walletIndex.toString() : '0' - }) - ) + upgradeAssetHandler({ asset, walletAddress, walletIndex, walletType, hdMode }) } return ( @@ -177,7 +165,7 @@ export const AssetsTableCollapsable: React.FC = (props): JSX.Element => { ) } }), - [haltThorChain, haltEthChain, haltBnbChain, network, intl, setSelectedAsset, navigate] + [haltThorChain, haltEthChain, haltBnbChain, network, intl, upgradeAssetHandler] ) const renderBalanceColumn = ({ asset, amount }: Balance) => { @@ -272,13 +260,15 @@ export const AssetsTableCollapsable: React.FC = (props): JSX.Element => { oWalletAddress, loading = false, walletType, - walletIndex + walletIndex, + hdMode }: { tableData: Balance[] oWalletAddress: O.Option
loading?: boolean walletType: WalletType walletIndex: number + hdMode: HDMode }) => { return ( = (props): JSX.Element => { dataSource={tableData} loading={loading} rowKey={({ asset }) => asset.symbol} - onRow={onRowHandler(oWalletAddress, walletType, walletIndex)} + onRow={onRowHandler({ oWalletAddress, walletType, walletIndex, hdMode })} columns={columns} /> ) @@ -300,23 +290,32 @@ export const AssetsTableCollapsable: React.FC = (props): JSX.Element => { index, oWalletAddress, walletType, - walletIndex + walletIndex, + hdMode }: { balancesRD: WalletBalancesRD index: number oWalletAddress: O.Option
walletType: WalletType walletIndex: number + hdMode: HDMode }) => { return FP.pipe( balancesRD, RD.fold( // initial state - () => renderAssetsTable({ tableData: [], oWalletAddress, loading: false, walletType, walletIndex }), + () => renderAssetsTable({ tableData: [], oWalletAddress, loading: false, walletType, walletIndex, hdMode }), // loading state () => { const data = previousAssetsTableData.current[index] ?? [] - return renderAssetsTable({ tableData: data, oWalletAddress, loading: true, walletType, walletIndex }) + return renderAssetsTable({ + tableData: data, + oWalletAddress, + loading: true, + walletType, + walletIndex, + hdMode + }) }, // error state ({ msg }: ApiError) => { @@ -326,7 +325,14 @@ export const AssetsTableCollapsable: React.FC = (props): JSX.Element => { (balances) => { const prev = previousAssetsTableData.current prev[index] = balances - return renderAssetsTable({ tableData: balances, oWalletAddress, loading: false, walletType, walletIndex }) + return renderAssetsTable({ + tableData: balances, + oWalletAddress, + loading: false, + walletType, + walletIndex, + hdMode + }) } ) ) @@ -337,7 +343,7 @@ export const AssetsTableCollapsable: React.FC = (props): JSX.Element => { // Panel const renderPanel = useCallback( ( - { chain, walletType, walletAddress: oWalletAddress, balances: balancesRD, walletIndex }: ChainBalance, + { chain, walletType, walletAddress: oWalletAddress, balances: balancesRD, walletIndex, hdMode }: ChainBalance, key: number ) => { /** @@ -415,7 +421,8 @@ export const AssetsTableCollapsable: React.FC = (props): JSX.Element => { index: key, oWalletAddress, walletType, - walletIndex + walletIndex, + hdMode })} ) diff --git a/src/renderer/components/wallet/txs/send/SendFormBCH.stories.tsx b/src/renderer/components/wallet/txs/send/SendFormBCH.stories.tsx index c6e043f21..ddc4c1232 100644 --- a/src/renderer/components/wallet/txs/send/SendFormBCH.stories.tsx +++ b/src/renderer/components/wallet/txs/send/SendFormBCH.stories.tsx @@ -74,6 +74,7 @@ const Template = ({ txRDStatus, feeRDStatus, balance, validAddress, walletType } = (props): JSX.Element => { const { - walletType, - walletIndex, - walletAddress, + asset: { walletType, walletIndex, hdMode, walletAddress }, balances, balance, transfer$, @@ -284,6 +280,7 @@ export const SendFormBCH: React.FC = (props): JSX.Element => { transfer$({ walletType, walletIndex, + hdMode, sender: walletAddress, recipient: form.getFieldValue('recipient'), asset, @@ -297,6 +294,7 @@ export const SendFormBCH: React.FC = (props): JSX.Element => { transfer$, walletType, walletIndex, + hdMode, walletAddress, form, asset, diff --git a/src/renderer/components/wallet/txs/send/SendFormBNB.stories.tsx b/src/renderer/components/wallet/txs/send/SendFormBNB.stories.tsx index 84dd2d9f0..4271f5d2d 100644 --- a/src/renderer/components/wallet/txs/send/SendFormBNB.stories.tsx +++ b/src/renderer/components/wallet/txs/send/SendFormBNB.stories.tsx @@ -60,6 +60,7 @@ const Template = ({ txRDStatus, feeRDStatus, balance, validAddress, walletType } = (props): JSX.Element => { const { - walletType, balances, balance, - walletAddress, - walletIndex, + asset: { walletType, walletAddress, walletIndex, hdMode }, transfer$, openExplorerTxUrl, getExplorerTxUrl, @@ -197,6 +193,7 @@ export const SendFormBNB: React.FC = (props): JSX.Element => { transfer$({ walletType, walletIndex, + hdMode, sender: walletAddress, recipient: form.getFieldValue('recipient'), asset, @@ -204,7 +201,7 @@ export const SendFormBNB: React.FC = (props): JSX.Element => { memo: form.getFieldValue('memo') }) ) - }, [asset, subscribeSendTxState, transfer$, walletType, walletIndex, walletAddress, form, amountToSend]) + }, [asset, subscribeSendTxState, transfer$, walletType, walletIndex, hdMode, walletAddress, form, amountToSend]) const renderConfirmationModal = useMemo(() => { const onSuccessHandler = () => { diff --git a/src/renderer/components/wallet/txs/send/SendFormBTC.stories.tsx b/src/renderer/components/wallet/txs/send/SendFormBTC.stories.tsx index 19577b85c..1297a4388 100644 --- a/src/renderer/components/wallet/txs/send/SendFormBTC.stories.tsx +++ b/src/renderer/components/wallet/txs/send/SendFormBTC.stories.tsx @@ -75,6 +75,7 @@ const Template = ({ txRDStatus, feeRDStatus, balance, validAddress, walletType } = (props): JSX.Element => { const { - walletType, - walletIndex, - walletAddress, + asset: { walletType, walletIndex, hdMode, walletAddress }, balances, balance, transfer$, @@ -284,6 +280,7 @@ export const SendFormBTC: React.FC = (props): JSX.Element => { transfer$({ walletType, walletIndex, + hdMode, sender: walletAddress, recipient: form.getFieldValue('recipient'), asset: asset, @@ -297,6 +294,7 @@ export const SendFormBTC: React.FC = (props): JSX.Element => { transfer$, walletType, walletIndex, + hdMode, walletAddress, form, asset, diff --git a/src/renderer/components/wallet/txs/send/SendFormCOSMOS.tsx b/src/renderer/components/wallet/txs/send/SendFormCOSMOS.tsx index 44b1bd67e..eadcd62bf 100644 --- a/src/renderer/components/wallet/txs/send/SendFormCOSMOS.tsx +++ b/src/renderer/components/wallet/txs/send/SendFormCOSMOS.tsx @@ -47,8 +47,7 @@ export type FormValues = { } export type Props = { - walletType: WalletType - walletIndex: number + asset: SelectedWalletAsset balances: WalletBalances balance: WalletBalance transfer$: SendTxStateHandler @@ -63,8 +62,7 @@ export type Props = { export const SendFormCOSMOS: React.FC = (props): JSX.Element => { const { - walletType, - walletIndex, + asset: { walletType, walletIndex, hdMode }, balances, balance, transfer$, @@ -184,13 +182,14 @@ export const SendFormCOSMOS: React.FC = (props): JSX.Element => { transfer$({ walletType, walletIndex, + hdMode, recipient: form.getFieldValue('recipient'), asset, amount: amountToSend, memo: form.getFieldValue('memo') }) ) - }, [subscribeSendTxState, transfer$, walletType, walletIndex, form, asset, amountToSend]) + }, [subscribeSendTxState, transfer$, walletType, walletIndex, hdMode, form, asset, amountToSend]) const [showConfirmationModal, setShowConfirmationModal] = useState(false) diff --git a/src/renderer/components/wallet/txs/send/SendFormDOGE.stories.tsx b/src/renderer/components/wallet/txs/send/SendFormDOGE.stories.tsx index 6f359285f..36fb1531e 100644 --- a/src/renderer/components/wallet/txs/send/SendFormDOGE.stories.tsx +++ b/src/renderer/components/wallet/txs/send/SendFormDOGE.stories.tsx @@ -75,6 +75,7 @@ const Template = ({ txRDStatus, feeRDStatus, balance, validAddress, walletType } = (props): JSX.Element => { const { - walletType, - walletIndex, - walletAddress, + asset: { walletType, walletIndex, hdMode, walletAddress }, balances, balance, transfer$, @@ -254,6 +250,7 @@ export const SendFormDOGE: React.FC = (props): JSX.Element => { transfer$({ walletType, walletIndex, + hdMode, sender: walletAddress, recipient: form.getFieldValue('recipient'), asset, @@ -267,6 +264,7 @@ export const SendFormDOGE: React.FC = (props): JSX.Element => { transfer$, walletType, walletIndex, + hdMode, walletAddress, form, asset, diff --git a/src/renderer/components/wallet/txs/send/SendFormETH.stories.tsx b/src/renderer/components/wallet/txs/send/SendFormETH.stories.tsx index 9406bb878..b06022527 100644 --- a/src/renderer/components/wallet/txs/send/SendFormETH.stories.tsx +++ b/src/renderer/components/wallet/txs/send/SendFormETH.stories.tsx @@ -8,7 +8,7 @@ import * as Rx from 'rxjs' import { getMockRDValueFactory, RDStatus } from '../../../../../shared/mock/rdByStatus' import { mockValidatePassword$ } from '../../../../../shared/mock/wallet' -import { WalletType } from '../../../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../../../shared/wallet/types' import { THORCHAIN_DECIMAL } from '../../../../helpers/assetHelper' import { mockWalletBalance } from '../../../../helpers/test/testWalletHelper' import { FeesRD, SendTxStateHandler } from '../../../../services/chain/types' @@ -20,9 +20,10 @@ type Args = { feeRDStatus: RDStatus balance: string walletType: WalletType + hdMode: HDMode } -const Template = ({ txRDStatus, feeRDStatus, balance, walletType }: Args) => { +const Template = ({ txRDStatus, feeRDStatus, balance, walletType, hdMode }: Args) => { const transfer$: SendTxStateHandler = (_) => Rx.of({ steps: { current: txRDStatus === 'initial' ? 0 : 1, total: 1 }, @@ -65,6 +66,7 @@ const Template = ({ txRDStatus, feeRDStatus, balance, walletType }: Args) => { = { walletType: { control: { type: 'select', options: ['keystore', 'ledger'] } }, + hdMode: { + control: { type: 'select', options: ['default', 'ledgerlive', 'metamask', 'legacy'] } + }, balance: { control: { type: 'text' } } diff --git a/src/renderer/components/wallet/txs/send/SendFormETH.tsx b/src/renderer/components/wallet/txs/send/SendFormETH.tsx index 2aaa25a01..62622a73a 100644 --- a/src/renderer/components/wallet/txs/send/SendFormETH.tsx +++ b/src/renderer/components/wallet/txs/send/SendFormETH.tsx @@ -33,7 +33,7 @@ import { useSubscriptionState } from '../../../../hooks/useSubscriptionState' import { INITIAL_SEND_STATE } from '../../../../services/chain/const' import { SendTxState, SendTxStateHandler } from '../../../../services/chain/types' import { FeesRD, GetExplorerTxUrl, OpenExplorerTxUrl, WalletBalances } from '../../../../services/clients' -import { ValidatePasswordHandler } from '../../../../services/wallet/types' +import { SelectedWalletAsset, ValidatePasswordHandler } from '../../../../services/wallet/types' import { WalletBalance } from '../../../../services/wallet/types' import { LedgerConfirmationModal, WalletPasswordConfirmationModal } from '../../../modal/confirmation' import * as StyledR from '../../../shared/form/Radio.styles' @@ -54,9 +54,7 @@ export type FormValues = { } export type Props = { - walletType: WalletType - walletIndex: number - walletAddress: Address + asset: SelectedWalletAsset balances: WalletBalances balance: WalletBalance transfer$: SendTxStateHandler @@ -70,9 +68,7 @@ export type Props = { export const SendFormETH: React.FC = (props): JSX.Element => { const { - walletType, - walletIndex, - walletAddress, + asset: { walletType, walletIndex, hdMode, walletAddress }, balances, balance, transfer$, @@ -321,6 +317,7 @@ export const SendFormETH: React.FC = (props): JSX.Element => { transfer$({ walletType, walletIndex, + hdMode, sender: walletAddress, recipient, asset, @@ -339,6 +336,7 @@ export const SendFormETH: React.FC = (props): JSX.Element => { transfer$, walletType, walletIndex, + hdMode, walletAddress, asset, selectedFeeOption, diff --git a/src/renderer/components/wallet/txs/send/SendFormLTC.stories.tsx b/src/renderer/components/wallet/txs/send/SendFormLTC.stories.tsx index cfae9d8cc..14208f665 100644 --- a/src/renderer/components/wallet/txs/send/SendFormLTC.stories.tsx +++ b/src/renderer/components/wallet/txs/send/SendFormLTC.stories.tsx @@ -74,6 +74,7 @@ const Template = ({ txRDStatus, feeRDStatus, balance, validAddress, walletType } = (props): JSX.Element => { const { - walletType, - walletIndex, - walletAddress, + asset: { walletType, walletIndex, hdMode, walletAddress }, balances, balance, transfer$, @@ -281,6 +277,7 @@ export const SendFormLTC: React.FC = (props): JSX.Element => { transfer$({ walletType, walletIndex, + hdMode, sender: walletAddress, recipient: form.getFieldValue('recipient'), asset, @@ -294,6 +291,7 @@ export const SendFormLTC: React.FC = (props): JSX.Element => { transfer$, walletType, walletIndex, + hdMode, walletAddress, form, asset, diff --git a/src/renderer/components/wallet/txs/send/SendFormTHOR.stories.tsx b/src/renderer/components/wallet/txs/send/SendFormTHOR.stories.tsx index 85ed39a67..2c4bbdf1f 100644 --- a/src/renderer/components/wallet/txs/send/SendFormTHOR.stories.tsx +++ b/src/renderer/components/wallet/txs/send/SendFormTHOR.stories.tsx @@ -53,6 +53,7 @@ const Template = ({ txRDStatus, feeRDStatus, balance, validAddress, walletType } = (props): JSX.Element => { const { - walletType, - walletIndex, + asset: { walletType, walletIndex, hdMode }, + balances, balance, transfer$, @@ -199,10 +198,11 @@ export const SendFormTHOR: React.FC = (props): JSX.Element => { recipient: form.getFieldValue('recipient'), asset, amount: amountToSend, - memo: form.getFieldValue('memo') + memo: form.getFieldValue('memo'), + hdMode }) ) - }, [subscribeSendTxState, transfer$, walletType, walletIndex, form, asset, amountToSend]) + }, [subscribeSendTxState, transfer$, walletType, walletIndex, hdMode, form, asset, amountToSend]) const [showConfirmationModal, setShowConfirmationModal] = useState(false) diff --git a/src/renderer/components/wallet/txs/upgrade/Upgrade.stories.tsx b/src/renderer/components/wallet/txs/upgrade/Upgrade.stories.tsx index 36bfa6caa..6b5ff74c4 100644 --- a/src/renderer/components/wallet/txs/upgrade/Upgrade.stories.tsx +++ b/src/renderer/components/wallet/txs/upgrade/Upgrade.stories.tsx @@ -107,6 +107,7 @@ const Template = ({ txRDStatus, feeRDStatus, balance, hasLedger, walletType, val addressValidation={(_: string) => validAddress} walletType={walletType} walletIndex={0} + hdMode="default" targetPoolAddressRD={RD.success({ chain: BNBChain, address: 'bnb-pool-address', router: O.none, halted: false })} validatePassword$={mockValidatePassword$} fee={feeRD} diff --git a/src/renderer/components/wallet/txs/upgrade/Upgrade.tsx b/src/renderer/components/wallet/txs/upgrade/Upgrade.tsx index 95fec38ad..4ef40d6ae 100644 --- a/src/renderer/components/wallet/txs/upgrade/Upgrade.tsx +++ b/src/renderer/components/wallet/txs/upgrade/Upgrade.tsx @@ -78,7 +78,8 @@ export const Upgrade: React.FC = (props): JSX.Element => { network, walletAddress, walletType, - walletIndex + walletIndex, + hdMode } = props const intl = useIntl() @@ -538,7 +539,8 @@ export const Upgrade: React.FC = (props): JSX.Element => { amount: assetToBase(assetAmount(0)), asset: runeAsset.asset, walletAddress: '', - walletIndex + walletIndex, + hdMode }} walletBalances={[]} network={network} diff --git a/src/renderer/contexts/EthereumContext.tsx b/src/renderer/contexts/EthereumContext.tsx index 521d242ae..971960a69 100644 --- a/src/renderer/contexts/EthereumContext.tsx +++ b/src/renderer/contexts/EthereumContext.tsx @@ -6,7 +6,7 @@ import * as Rx from 'rxjs' import * as RxOp from 'rxjs/operators' import { DEFAULT_ETH_DERIVATION_MODE } from '../../shared/ethereum/const' -import { EthDerivationMode } from '../../shared/ethereum/types' +import { EthHDMode } from '../../shared/ethereum/types' import { client$, clientState$, @@ -46,8 +46,8 @@ export type EthereumContextValue = { isApprovedERC20Token$: typeof isApprovedERC20Token$ approveFee$: typeof approveFee$ reloadApproveFee: typeof reloadApproveFee - ethDerivationMode$: Rx.Observable - updateEthDerivationMode: (m: EthDerivationMode) => void + ethDerivationMode$: Rx.Observable + updateEthDerivationMode: (m: EthHDMode) => void } export const ethDerivationMode$ = FP.pipe( @@ -61,7 +61,7 @@ export const ethDerivationMode$ = FP.pipe( RxOp.distinctUntilChanged() ) -export const updateEthDerivationMode = (mode: EthDerivationMode) => { +export const updateEthDerivationMode = (mode: EthHDMode) => { modifyStorage(O.some({ ethDerivationMode: mode })) } diff --git a/src/renderer/helpers/fp/eq.ts b/src/renderer/helpers/fp/eq.ts index 53b3c69db..e84779faa 100644 --- a/src/renderer/helpers/fp/eq.ts +++ b/src/renderer/helpers/fp/eq.ts @@ -11,12 +11,12 @@ import * as O from 'fp-ts/lib/Option' import * as S from 'fp-ts/lib/string' import { KeystoreId, LedgerError, Network } from '../../../shared/api/types' -import { EthDerivationMode } from '../../../shared/ethereum/types' -import { WalletAddress, WalletType } from '../../../shared/wallet/types' +import { EthHDMode } from '../../../shared/ethereum/types' +import { HDMode, WalletAddress, WalletType } from '../../../shared/wallet/types' import { DepositAssetFees, DepositFees, SwapFeesParams, SymDepositAddresses } from '../../services/chain/types' import { ApproveParams } from '../../services/ethereum/types' import { PoolAddress, PoolShare } from '../../services/midgard/types' -import { ApiError, LedgerAddress, WalletBalance } from '../../services/wallet/types' +import { ApiError, LedgerAddress, SelectedWalletAsset, WalletBalance } from '../../services/wallet/types' import { AssetWithAmount } from '../../types/asgardex' import { PricePool } from '../../views/pools/Pools.types' @@ -108,12 +108,28 @@ export const eqAssetsWithBalanceRD = RD.getEq(eqApiError, e export const eqWalletType: Eq.Eq = eqString +export const eqEthDerivationMode: Eq.Eq = eqString +export const eqOEthDerivationMode = O.getEq(eqEthDerivationMode) + +export const eqHDMode: Eq.Eq = eqString +export const eqOHDMode = O.getEq(eqHDMode) + export const eqWalletBalance: Eq.Eq = { equals: (x, y) => eqBalance.equals(x, y) && x.walletAddress === y.walletAddress } export const eqOWalletBalance = O.getEq(eqWalletBalance) export const eqWalletBalances = A.getEq(eqWalletBalance) +export const eqSelectedWalletAsset = Eq.struct({ + asset: eqAsset, + walletAddress: eqAddress, + walletIndex: eqNumber, + walletType: eqWalletType, + hdMode: eqHDMode +}) + +export const eqOSelectedWalletAsset = O.getEq(eqSelectedWalletAsset) + export const eqPoolShare = Eq.struct({ asset: eqAsset, assetAddedAmount: eqBaseAmount, @@ -127,8 +143,8 @@ export const eqPoolShares = A.getEq(eqPoolShare) export const eqPoolAddresses = Eq.struct({ chain: eqChain, - address: eqString, - router: eqOString, + address: eqAddress, + router: eqOAddress, halted: eqBoolean }) @@ -141,11 +157,12 @@ export const eqSwapFeesParams = Eq.struct({ export const eqApproveParams = Eq.struct({ network: eqString, - spenderAddress: eqString, - contractAddress: eqString, - fromAddress: eqString, + spenderAddress: eqAddress, + contractAddress: eqAddress, + fromAddress: eqAddress, walletIndex: eqNumber, - walletType: eqWalletType + walletType: eqWalletType, + hdMode: eqHDMode }) export const eqOApproveParams = O.getEq(eqApproveParams) @@ -191,14 +208,13 @@ const eqLedgerError = Eq.struct({ }) export const eqKeystoreId: Eq.Eq = eqNumber -export const eqEthDerivationMode: Eq.Eq = eqString -export const eqOEthDerivationMode = O.getEq(eqEthDerivationMode) export const eqWalletAddress = Eq.struct({ - address: eqString, + address: eqAddress, type: eqString, chain: eqChain, - walletIndex: eqNumber + walletIndex: eqNumber, + hdMode: eqHDMode }) export const eqOWalletAddress = O.getEq(eqWalletAddress) @@ -209,9 +225,10 @@ export const eqLedgerAddress = Eq.struct({ keystoreId: eqKeystoreId, chain: eqChain, network: eqNetwork, - address: eqString, + address: eqAddress, walletIndex: eqNumber, - ethDerivationMode: eqOEthDerivationMode + hdMode: eqHDMode, + type: eqWalletType }) export const eqOLedgerAddress = O.getEq(eqLedgerAddress) diff --git a/src/renderer/helpers/fp/ord.test.ts b/src/renderer/helpers/fp/ord.test.ts index 1790eb8cf..5a207a260 100644 --- a/src/renderer/helpers/fp/ord.test.ts +++ b/src/renderer/helpers/fp/ord.test.ts @@ -44,7 +44,8 @@ describe('helpers/fp/ord', () => { amount: baseAmount('1'), asset: AssetRuneNative, walletAddress: '', - walletIndex: 0 + walletIndex: 0, + hdMode: 'default' } const b: WalletBalance = { ...a, diff --git a/src/renderer/helpers/test/testWalletHelper.ts b/src/renderer/helpers/test/testWalletHelper.ts index a341af3ac..cd91a386f 100644 --- a/src/renderer/helpers/test/testWalletHelper.ts +++ b/src/renderer/helpers/test/testWalletHelper.ts @@ -24,6 +24,7 @@ export const mockWalletBalance = (overrides?: Partial): WalletBal asset: AssetRuneNative, walletAddress: 'wallet-address', walletIndex: 0, + hdMode: 'default', ...overrides }) @@ -46,5 +47,6 @@ export const mockWalletAddress = (overrides?: Partial): WalletAdd type: 'keystore', chain: THORChain, walletIndex: 0, + hdMode: 'default', ...overrides }) diff --git a/src/renderer/hooks/useLedger.ts b/src/renderer/hooks/useLedger.ts index 582b57b4b..2ad19d2f8 100644 --- a/src/renderer/hooks/useLedger.ts +++ b/src/renderer/hooks/useLedger.ts @@ -7,7 +7,7 @@ import { useObservableState } from 'observable-hooks' import * as RxOp from 'rxjs/operators' import { KeystoreId } from '../../shared/api/types' -import { EthDerivationMode } from '../../shared/ethereum/types' +import { EthHDMode } from '../../shared/ethereum/types' import { WalletAddress } from '../../shared/wallet/types' import { useWalletContext } from '../contexts/WalletContext' import { LedgerAddress } from '../services/wallet/types' @@ -20,7 +20,7 @@ export const useLedger = (chain: Chain, id: KeystoreId) => { const { addLedgerAddress$, getLedgerAddress$, verifyLedgerAddress$, removeLedgerAddress } = useWalletContext() const verifyAddress = useCallback( - (walletIndex: number, ethDerivationMode: O.Option) => + (walletIndex: number, ethDerivationMode: O.Option) => verifyLedgerAddress$({ chain, network, walletIndex, ethDerivationMode }), [chain, verifyLedgerAddress$, network] ) @@ -40,7 +40,7 @@ export const useLedger = (chain: Chain, id: KeystoreId) => { ) const addAddress = useCallback( - (walletIndex: number, ethDerivationMode: O.Option) => + (walletIndex: number, ethDerivationMode: O.Option) => addLedgerAddress$({ id, chain, network, walletIndex, ethDerivationMode }), [addLedgerAddress$, chain, id, network] ) diff --git a/src/renderer/routes/wallet/wallet.test.ts b/src/renderer/routes/wallet/wallet.test.ts index 169409fec..16eccdf9d 100644 --- a/src/renderer/routes/wallet/wallet.test.ts +++ b/src/renderer/routes/wallet/wallet.test.ts @@ -71,97 +71,29 @@ describe('Wallet routes', () => { describe('asset detail route', () => { it('template', () => { - expect(assetDetail.template).toEqual('/wallet/assets/detail/:walletType/:walletAddress/:walletIndex/:asset') + expect(assetDetail.template).toEqual('/wallet/assets/detail') }) - it('returns path by given asset parameter', () => { - expect( - assetDetail.path({ walletType: 'keystore', asset: 'BNB.BNB', walletAddress: 'abc123', walletIndex: '0' }) - ).toEqual('/wallet/assets/detail/keystore/abc123/0/BNB.BNB') - }) - it('redirects to base path if asset is empty', () => { - expect( - assetDetail.path({ walletType: 'keystore', asset: '', walletAddress: 'abc123', walletIndex: '0' }) - ).toEqual(assets.path()) - }) - - it('redirects to base path if address is empty', () => { - expect( - assetDetail.path({ walletType: 'keystore', asset: 'some asset', walletAddress: '', walletIndex: '0' }) - ).toEqual(assets.path()) + it('path', () => { + expect(assetDetail.path()).toEqual('/wallet/assets/detail') }) }) describe('send route', () => { it('template', () => { - expect(send.template).toEqual('/wallet/assets/detail/:walletType/:walletAddress/:walletIndex/:asset/send') + expect(send.template).toEqual('/wallet/assets/detail/send') }) it('path ', () => { - expect( - send.path({ asset: 'BNB.BNB', walletAddress: 'bnb123address', walletType: 'keystore', walletIndex: '0' }) - ).toEqual('/wallet/assets/detail/keystore/bnb123address/0/BNB.BNB/send') - }) - it('redirects to base path if asset is empty', () => { - expect(send.path({ asset: '', walletAddress: 'some wallet', walletType: 'keystore', walletIndex: '0' })).toEqual( - assets.path() - ) - }) - - it('redirects to base path if address is empty', () => { - expect(send.path({ asset: 'some asset', walletAddress: '', walletType: 'keystore', walletIndex: '0' })).toEqual( - assets.path() - ) + expect(send.path()).toEqual('/wallet/assets/detail/send') }) }) describe('upgrade route', () => { it('template', () => { - expect(upgradeRune.template).toEqual( - '/wallet/assets/detail/:walletType/:walletAddress/:walletIndex/:asset/upgrade' - ) - }) - it('path for BNB.RUNE-67C ', () => { - expect( - upgradeRune.path({ - asset: 'BNB.RUNE-67C', - walletAddress: 'bnb123address', - network: 'testnet', - walletType: 'keystore', - walletIndex: '0' - }) - ).toEqual('/wallet/assets/detail/keystore/bnb123address/0/BNB.RUNE-67C/upgrade') - }) - it('path for BNB.RUNE-B1A ', () => { - expect( - upgradeRune.path({ - asset: 'BNB.RUNE-B1A', - walletAddress: 'bnb123address', - network: 'mainnet', - walletType: 'keystore', - walletIndex: '0' - }) - ).toEqual('/wallet/assets/detail/keystore/bnb123address/0/BNB.RUNE-B1A/upgrade') - }) - it('redirects to base path for BNB assets ', () => { - expect( - upgradeRune.path({ - asset: 'BNB.BNB', - walletAddress: 'walletAddress', - network: 'mainnet', - walletType: 'keystore', - walletIndex: '0' - }) - ).toEqual('/wallet/assets') - }) - it('redirects to base path for empty addresses ', () => { - expect( - upgradeRune.path({ - asset: 'BNB.RUNE-67C', - walletAddress: '', - network: 'testnet', - walletType: 'keystore', - walletIndex: '0' - }) - ).toEqual('/wallet/assets') + expect(upgradeRune.template).toEqual('/wallet/assets/detail/upgrade') + }) + + it('path ', () => { + expect(upgradeRune.path()).toEqual('/wallet/assets/detail/upgrade') }) }) @@ -176,37 +108,22 @@ describe('Wallet routes', () => { describe('interact route', () => { it('template', () => { - expect(interact.template).toEqual('/wallet/assets/interact/:interactType/:walletType/:walletAddress/:walletIndex') + expect(interact.template).toEqual('/wallet/assets/interact/:interactType') }) it('bond + keystore', () => { - expect( - interact.path({ interactType: 'bond', walletType: 'keystore', walletAddress: 'abc123', walletIndex: '0' }) - ).toEqual('/wallet/assets/interact/bond/keystore/abc123/0') + expect(interact.path({ interactType: 'bond' })).toEqual('/wallet/assets/interact/bond') }) it('bond + ledger + index 1', () => { - expect( - interact.path({ interactType: 'bond', walletType: 'ledger', walletAddress: 'abc123', walletIndex: '1' }) - ).toEqual('/wallet/assets/interact/bond/ledger/abc123/1') + expect(interact.path({ interactType: 'bond' })).toEqual('/wallet/assets/interact/bond') }) it('unbond', () => { - expect( - interact.path({ interactType: 'unbond', walletType: 'keystore', walletAddress: 'abc123', walletIndex: '0' }) - ).toEqual('/wallet/assets/interact/unbond/keystore/abc123/0') + expect(interact.path({ interactType: 'unbond' })).toEqual('/wallet/assets/interact/unbond') }) it('leave', () => { - expect( - interact.path({ interactType: 'leave', walletType: 'keystore', walletAddress: 'abc123', walletIndex: '0' }) - ).toEqual('/wallet/assets/interact/leave/keystore/abc123/0') + expect(interact.path({ interactType: 'leave' })).toEqual('/wallet/assets/interact/leave') }) it('custom', () => { - expect( - interact.path({ interactType: 'custom', walletType: 'keystore', walletAddress: 'abc123', walletIndex: '0' }) - ).toEqual('/wallet/assets/interact/custom/keystore/abc123/0') - }) - it('redirects for invalid values ', () => { - expect( - interact.path({ interactType: 'bond', walletAddress: '', walletType: 'ledger', walletIndex: '0' }) - ).toEqual('/wallet/assets') + expect(interact.path({ interactType: 'custom' })).toEqual('/wallet/assets/interact/custom') }) }) }) diff --git a/src/renderer/routes/wallet/wallet.ts b/src/renderer/routes/wallet/wallet.ts index 15370e642..ffffc9e73 100644 --- a/src/renderer/routes/wallet/wallet.ts +++ b/src/renderer/routes/wallet/wallet.ts @@ -1,13 +1,4 @@ -import { Address } from '@xchainjs/xchain-client' -import { assetFromString, assetToString } from '@xchainjs/xchain-util' -import * as FP from 'fp-ts/lib/function' -import * as O from 'fp-ts/lib/Option' - -import { Network } from '../../../shared/api/types' -import { WalletType } from '../../../shared/wallet/types' import { InteractType } from '../../components/wallet/txs/interact/Interact.types' -import { isNonNativeRuneAsset } from '../../helpers/assetHelper' -import { sequenceTOption } from '../../helpers/fpHelpers' import { Route } from '../types' export * as imports from './imports' @@ -55,19 +46,11 @@ export const poolShares: Route = { export type InteractParams = { interactType: InteractType - walletAddress: string - walletType: WalletType - walletIndex: string } export const interact: Route = { - template: `${assets.template}/interact/:interactType/:walletType/:walletAddress/:walletIndex`, - path({ interactType, walletType, walletAddress, walletIndex }) { - if (walletAddress) { - return `${assets.template}/interact/${interactType}/${walletType}/${walletAddress}/${walletIndex}` - } else { - // Redirect to assets route if passed param are invalid - return assets.path() - } + template: `${assets.template}/interact/:interactType`, + path({ interactType }) { + return `${assets.template}/interact/${interactType}` } } @@ -78,62 +61,24 @@ export const bonds: Route = { } } -export type AssetDetailsParams = { asset: string; walletAddress: Address; walletType: WalletType; walletIndex: string } -export const assetDetail: Route = { - template: `${assets.template}/detail/:walletType/:walletAddress/:walletIndex/:asset`, - path: ({ walletType, asset, walletAddress, walletIndex }) => { - if (asset && !!walletAddress) { - return `${assets.template}/detail/${walletType}/${walletAddress}/${walletIndex}/${asset}` - } else { - // Redirect to assets route if passed param is empty - return assets.path() - } +export const assetDetail: Route = { + template: `${assets.template}/detail`, + path() { + return this.template } } -export type SendParams = { asset: string; walletAddress: Address; walletType: WalletType; walletIndex: string } -export const send: Route = { +export const send: Route = { template: `${assetDetail.template}/send`, - path: ({ asset, walletAddress, walletType, walletIndex }) => { - if (asset && !!walletAddress) { - return `${assetDetail.path({ walletType, asset, walletAddress, walletIndex })}/send` - } else { - // Redirect to assets route if passed params are empty - return assets.path() - } + path() { + return this.template } } -export type AssetUpgradeDetailsParams = { - asset: string - walletAddress: string - network: Network - walletType: WalletType - walletIndex: string -} -export const upgradeRune: Route = { +export const upgradeRune: Route = { template: `${assetDetail.template}/upgrade`, - path: ({ asset: assetString, walletAddress, network, walletType, walletIndex }) => { - // Validate asset string to accept BNB.Rune only - const oAsset = FP.pipe( - assetFromString(assetString), - O.fromNullable, - O.filter((asset) => isNonNativeRuneAsset(asset, network)) - ) - // Simple validation of address - const oWalletAddress = FP.pipe( - walletAddress, - O.fromPredicate((s: string) => s.length > 0) - ) - return FP.pipe( - sequenceTOption(oAsset, oWalletAddress, O.some(walletIndex)), - O.fold( - // Redirect to assets route if passed params are empty - () => assets.path(), - ([asset, walletAddress, walletIndex]) => - `${assetDetail.path({ walletType, asset: assetToString(asset), walletAddress, walletIndex })}/upgrade` - ) - ) + path() { + return this.template } } diff --git a/src/renderer/services/binance/transaction.ts b/src/renderer/services/binance/transaction.ts index 9ca47f849..ce219ad7b 100644 --- a/src/renderer/services/binance/transaction.ts +++ b/src/renderer/services/binance/transaction.ts @@ -33,7 +33,8 @@ export const createTransactionService = (client$: Client$, network$: Network$): feeRate: NaN, feeOption: undefined, feeAmount: undefined, - nodeUrl: undefined + nodeUrl: undefined, + hdMode: 'default' } const encoded = ipcLedgerSendTxParamsIO.encode(sendLedgerTxParams) diff --git a/src/renderer/services/bitcoin/balances.ts b/src/renderer/services/bitcoin/balances.ts index 3065394b1..e0533c1bc 100644 --- a/src/renderer/services/bitcoin/balances.ts +++ b/src/renderer/services/bitcoin/balances.ts @@ -1,4 +1,4 @@ -import { WalletBalanceType, WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletBalanceType, WalletType } from '../../../shared/wallet/types' import { observableState } from '../../helpers/stateHelper' import * as C from '../clients' import { client$ } from './common' @@ -19,11 +19,18 @@ const reloadBalances = () => { } // State of balances loaded by Client -const balances$ = ( - walletType: WalletType, - walletIndex: number, +const balances$ = ({ + walletType, + walletIndex, + walletBalanceType, + hdMode +}: { + walletType: WalletType + walletIndex: number walletBalanceType: WalletBalanceType -): C.WalletBalancesLD => C.balances$({ client$, trigger$: reloadBalances$, walletType, walletIndex, walletBalanceType }) + hdMode: HDMode +}): C.WalletBalancesLD => + C.balances$({ client$, trigger$: reloadBalances$, walletType, walletIndex, hdMode, walletBalanceType }) // State of balances loaded by Client and Address const getBalanceByAddress$ = (walletBalanceType: WalletBalanceType) => diff --git a/src/renderer/services/bitcoin/transaction.ts b/src/renderer/services/bitcoin/transaction.ts index 44292fc45..3889590db 100644 --- a/src/renderer/services/bitcoin/transaction.ts +++ b/src/renderer/services/bitcoin/transaction.ts @@ -33,7 +33,8 @@ export const createTransactionService = (client$: Client$, network$: Network$): recipient, memo, walletIndex, - nodeUrl: undefined + nodeUrl: undefined, + hdMode: 'default' } const encoded = ipcLedgerSendTxParamsIO.encode(sendLedgerTxParams) diff --git a/src/renderer/services/bitcoincash/balances.ts b/src/renderer/services/bitcoincash/balances.ts index e4d1ba034..8b410e239 100644 --- a/src/renderer/services/bitcoincash/balances.ts +++ b/src/renderer/services/bitcoincash/balances.ts @@ -1,4 +1,4 @@ -import { WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../shared/wallet/types' import { observableState } from '../../helpers/stateHelper' import * as C from '../clients' import { client$ } from './common' @@ -19,8 +19,23 @@ const reloadBalances = () => { } // State of balances loaded by Client -const balances$ = (walletType: WalletType, walletIndex: number): C.WalletBalancesLD => - C.balances$({ client$, trigger$: reloadBalances$, walletType, walletIndex, walletBalanceType: 'all' }) +const balances$ = ({ + walletType, + walletIndex, + hdMode +}: { + walletType: WalletType + walletIndex: number + hdMode: HDMode +}): C.WalletBalancesLD => + C.balances$({ + client$, + trigger$: reloadBalances$, + walletType, + walletIndex, + hdMode, + walletBalanceType: 'all' + }) // State of balances loaded by Client and Address const getBalanceByAddress$ = C.balancesByAddress$({ client$, trigger$: reloadBalances$, walletBalanceType: 'all' }) diff --git a/src/renderer/services/bitcoincash/transaction.ts b/src/renderer/services/bitcoincash/transaction.ts index c236fcdf2..7c18ba09c 100644 --- a/src/renderer/services/bitcoincash/transaction.ts +++ b/src/renderer/services/bitcoincash/transaction.ts @@ -33,7 +33,8 @@ export const createTransactionService = (client$: Client$, network$: Network$): recipient, memo, walletIndex, - nodeUrl: undefined + nodeUrl: undefined, + hdMode: 'default' } const encoded = ipcLedgerSendTxParamsIO.encode(sendLedgerTxParams) diff --git a/src/renderer/services/chain/transaction/common.ts b/src/renderer/services/chain/transaction/common.ts index c5e261a58..82154d7d1 100644 --- a/src/renderer/services/chain/transaction/common.ts +++ b/src/renderer/services/chain/transaction/common.ts @@ -47,7 +47,8 @@ export const sendTx$ = ({ amount, memo, feeOption = DEFAULT_FEE_OPTION, - walletIndex + walletIndex, + hdMode }: SendTxParams): TxHashLD => { switch (asset.chain) { case BNBChain: @@ -61,12 +62,12 @@ export const sendTx$ = ({ msg: error?.message ?? error.toString() })), liveData.chain(({ rates }) => - BTC.sendTx({ walletType, recipient, amount, feeRate: rates[feeOption], memo, walletIndex, sender }) + BTC.sendTx({ walletType, recipient, amount, feeRate: rates[feeOption], memo, walletIndex, hdMode, sender }) ) ) case ETHChain: - return ETH.sendTx({ walletType, asset, recipient, amount, memo, feeOption, walletIndex }) + return ETH.sendTx({ walletType, asset, recipient, amount, memo, feeOption, walletIndex, hdMode }) case THORChain: return THOR.sendTx({ walletType, amount, asset, memo, recipient, walletIndex }) @@ -134,6 +135,7 @@ export const sendPoolTx$ = ({ sender, walletType, walletIndex, + hdMode, router, asset, recipient, @@ -151,11 +153,12 @@ export const sendPoolTx$ = ({ amount, memo, walletIndex, + hdMode, feeOption }) case THORChain: - return THOR.sendPoolTx$({ walletType, amount, asset, memo, walletIndex }) + return THOR.sendPoolTx$({ walletType, amount, asset, memo, walletIndex, hdMode }) default: return sendTx$({ sender, walletType, asset, recipient, amount, memo, feeOption, walletIndex }) diff --git a/src/renderer/services/chain/transaction/withdraw.ts b/src/renderer/services/chain/transaction/withdraw.ts index 20048a861..8575cfa39 100644 --- a/src/renderer/services/chain/transaction/withdraw.ts +++ b/src/renderer/services/chain/transaction/withdraw.ts @@ -28,7 +28,7 @@ const { pools: midgardPoolsService, validateNode$ } = midgardService * @returns WithdrawState$ - Observable state to reflect loading status. It provides all data we do need to display status in `TxModal` * */ -export const symWithdraw$ = ({ memo, network, walletType, walletIndex }: SymWithdrawParams): WithdrawState$ => { +export const symWithdraw$ = ({ memo, network, walletType, walletIndex, hdMode }: SymWithdrawParams): WithdrawState$ => { // total of progress const total = O.some(100) @@ -54,6 +54,7 @@ export const symWithdraw$ = ({ memo, network, walletType, walletIndex }: SymWith return sendPoolTx$({ walletType, walletIndex, + hdMode, router: O.none, // no router for RUNE asset: AssetRuneNative, recipient: '', // empty for RUNE txs @@ -140,6 +141,7 @@ export const asymWithdraw$ = ({ memo, network, walletIndex, + hdMode, walletType }: AsymWithdrawParams): WithdrawState$ => { // total of progress @@ -176,6 +178,7 @@ export const asymWithdraw$ = ({ return sendTx$({ walletType, walletIndex, + hdMode, asset, recipient: poolAddress.address, // it will be empty string for RUNE amount: smallestAmountToSent(asset.chain, network), diff --git a/src/renderer/services/chain/types.ts b/src/renderer/services/chain/types.ts index 0a39228a1..9e786e68f 100644 --- a/src/renderer/services/chain/types.ts +++ b/src/renderer/services/chain/types.ts @@ -6,7 +6,7 @@ import * as O from 'fp-ts/lib/Option' import * as Rx from 'rxjs' import { Network } from '../../../shared/api/types' -import { WalletType, WalletAddress } from '../../../shared/wallet/types' +import { WalletType, WalletAddress, HDMode } from '../../../shared/wallet/types' import { LiveData } from '../../helpers/rx/liveData' import { AssetWithDecimal } from '../../types/asgardex' import { AssetWithAmount } from '../../types/asgardex' @@ -85,15 +85,6 @@ export type SymDepositParams = { readonly assetSender: Address } -export type SendDepositTxParams = { - walletType: WalletType - chain: Chain - asset: Asset - poolAddress: string - amount: BaseAmount - memo: Memo -} - export type SendTxParams = { walletType: WalletType asset: Asset @@ -106,6 +97,7 @@ export type SendTxParams = { feeAsset?: Asset gasLimit?: BigNumber feeAmount?: BaseAmount + hdMode: HDMode } export type SendPoolTxParams = SendTxParams & { @@ -261,6 +253,7 @@ export type SymWithdrawParams = { readonly network: Network readonly walletType: WalletType readonly walletIndex: number + readonly hdMode: HDMode } export type SymWithdrawStateHandler = (p: SymWithdrawParams) => WithdrawState$ @@ -272,6 +265,7 @@ export type AsymWithdrawParams = { readonly network: Network readonly walletType: WalletType readonly walletIndex: number + readonly hdMode: HDMode } export type AsymWithdrawStateHandler = (p: AsymWithdrawParams) => WithdrawState$ diff --git a/src/renderer/services/clients/address.ts b/src/renderer/services/clients/address.ts index 1f6848863..d7c566a4f 100644 --- a/src/renderer/services/clients/address.ts +++ b/src/renderer/services/clients/address.ts @@ -28,7 +28,8 @@ export const addressUI$: (client$: XChainClient$, chain: Chain) => WalletAddress address, chain, type: 'keystore', - walletIndex: 0 /* As long as we don't have HD wallets introduced, keystore will be always 0 */ + walletIndex: 0 /* As long as we don't have HD wallets introduced, keystore will be always 0 */, + hdMode: 'default' })) ) ), diff --git a/src/renderer/services/clients/balances.ts b/src/renderer/services/clients/balances.ts index 3a6724121..88e51901b 100644 --- a/src/renderer/services/clients/balances.ts +++ b/src/renderer/services/clients/balances.ts @@ -8,7 +8,7 @@ import * as Rx from 'rxjs' import * as RxOp from 'rxjs/operators' import { catchError, startWith, map, shareReplay, debounceTime } from 'rxjs/operators' -import { WalletBalanceType, WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletBalanceType, WalletType } from '../../../shared/wallet/types' import { liveData } from '../../helpers/rx/liveData' import { ApiError, ErrorId } from '../wallet/types' import { WalletBalancesLD, XChainClient$ } from './types' @@ -35,6 +35,7 @@ const loadBalances$ = ({ address, assets, walletIndex, + hdMode, walletBalanceType }: { client: C @@ -42,6 +43,7 @@ const loadBalances$ = ({ address?: Address assets?: Asset[] walletIndex: number + hdMode: HDMode walletBalanceType: WalletBalanceType }): WalletBalancesLD => FP.pipe( @@ -60,7 +62,8 @@ const loadBalances$ = ({ ...balance, walletType, walletAddress, - walletIndex + walletIndex, + hdMode })) ), catchError((error: Error) => @@ -77,6 +80,7 @@ type Balances$ = ({ trigger$, assets, walletIndex, + hdMode, walletBalanceType }: { walletType: WalletType @@ -84,6 +88,7 @@ type Balances$ = ({ trigger$: Rx.Observable assets?: Asset[] walletIndex: number + hdMode: HDMode walletBalanceType: WalletBalanceType }) => WalletBalancesLD /** @@ -95,7 +100,15 @@ type Balances$ = ({ * * If a client is not available (e.g. by removing keystore), it returns an `initial` state */ -export const balances$: Balances$ = ({ walletType, client$, trigger$, assets, walletIndex, walletBalanceType }) => +export const balances$: Balances$ = ({ + walletType, + client$, + trigger$, + assets, + walletIndex, + hdMode, + walletBalanceType +}) => Rx.combineLatest([trigger$.pipe(debounceTime(300)), client$]).pipe( RxOp.switchMap(([_, oClient]) => { return FP.pipe( @@ -110,6 +123,7 @@ export const balances$: Balances$ = ({ walletType, client$, trigger$, assets, wa client, assets, walletIndex, + hdMode, walletBalanceType }) ) @@ -130,16 +144,18 @@ type BalancesByAddress$ = ({ }) => ({ address, walletType, - walletIndex + walletIndex, + hdMode }: { address: string walletType: WalletType walletIndex: number + hdMode: HDMode }) => WalletBalancesLD export const balancesByAddress$: BalancesByAddress$ = ({ client$, trigger$, assets, walletBalanceType }) => - ({ address, walletType, walletIndex }) => + ({ address, walletType, walletIndex, hdMode }) => Rx.combineLatest([trigger$.pipe(debounceTime(300)), client$]).pipe( RxOp.mergeMap(([_, oClient]) => { return FP.pipe( @@ -148,7 +164,7 @@ export const balancesByAddress$: BalancesByAddress$ = // if a client is not available, "reset" state to "initial" () => Rx.of(RD.initial), // or start request and return state - (client) => loadBalances$({ client, address, walletType, assets, walletIndex, walletBalanceType }) + (client) => loadBalances$({ client, address, walletType, assets, walletIndex, walletBalanceType, hdMode }) ) ) }), diff --git a/src/renderer/services/cosmos/balances.ts b/src/renderer/services/cosmos/balances.ts index 301888c04..d1e31cbf1 100644 --- a/src/renderer/services/cosmos/balances.ts +++ b/src/renderer/services/cosmos/balances.ts @@ -1,6 +1,6 @@ import { AssetAtom } from '@xchainjs/xchain-cosmos' -import { WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../shared/wallet/types' import { observableState } from '../../helpers/stateHelper' import * as C from '../clients' import { client$ } from './common' @@ -21,12 +21,21 @@ const reloadBalances = () => { } // State of balances loaded by Client -const balances$ = (walletType: WalletType, walletIndex: number): C.WalletBalancesLD => +const balances$ = ({ + walletType, + walletIndex, + hdMode +}: { + walletType: WalletType + walletIndex: number + hdMode: HDMode +}): C.WalletBalancesLD => C.balances$({ client$, trigger$: reloadBalances$, walletType, walletIndex, + hdMode, walletBalanceType: 'all', // ATOM only - no IBC assets etc. assets: [AssetAtom] diff --git a/src/renderer/services/cosmos/transaction.ts b/src/renderer/services/cosmos/transaction.ts index 9a00bbfee..dc9a08444 100644 --- a/src/renderer/services/cosmos/transaction.ts +++ b/src/renderer/services/cosmos/transaction.ts @@ -56,7 +56,8 @@ export const createTransactionService = (client$: Client$, network$: Network$): feeRate: NaN, feeOption: undefined, feeAmount, - nodeUrl: undefined + nodeUrl: undefined, + hdMode: 'default' } const encoded = ipcLedgerSendTxParamsIO.encode(sendLedgerTxParams) diff --git a/src/renderer/services/doge/balances.ts b/src/renderer/services/doge/balances.ts index 78fc313d5..f858e0c24 100644 --- a/src/renderer/services/doge/balances.ts +++ b/src/renderer/services/doge/balances.ts @@ -1,4 +1,4 @@ -import { WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../shared/wallet/types' import { observableState } from '../../helpers/stateHelper' import * as C from '../clients' import { client$ } from './common' @@ -19,8 +19,16 @@ const reloadBalances = () => { } // State of balances loaded by Client -const balances$ = (walletType: WalletType, walletIndex: number): C.WalletBalancesLD => - C.balances$({ client$, trigger$: reloadBalances$, walletType, walletIndex, walletBalanceType: 'all' }) +const balances$ = ({ + walletType, + walletIndex, + hdMode +}: { + walletType: WalletType + walletIndex: number + hdMode: HDMode +}): C.WalletBalancesLD => + C.balances$({ client$, trigger$: reloadBalances$, walletType, walletIndex, hdMode, walletBalanceType: 'all' }) // State of balances loaded by Client and Address const getBalanceByAddress$ = C.balancesByAddress$({ client$, trigger$: reloadBalances$, walletBalanceType: 'all' }) diff --git a/src/renderer/services/doge/transaction.ts b/src/renderer/services/doge/transaction.ts index 9f4024679..3a5fc0139 100644 --- a/src/renderer/services/doge/transaction.ts +++ b/src/renderer/services/doge/transaction.ts @@ -33,7 +33,8 @@ export const createTransactionService = (client$: Client$, network$: Network$): recipient, memo, walletIndex, - nodeUrl: undefined + nodeUrl: undefined, + hdMode: 'default' } const encoded = ipcLedgerSendTxParamsIO.encode(sendLedgerTxParams) diff --git a/src/renderer/services/ethereum/balances.ts b/src/renderer/services/ethereum/balances.ts index 6d7da744e..6516f25b7 100644 --- a/src/renderer/services/ethereum/balances.ts +++ b/src/renderer/services/ethereum/balances.ts @@ -3,7 +3,7 @@ import * as A from 'fp-ts/lib/Array' import * as FP from 'fp-ts/lib/function' import { Network } from '../../../shared/api/types' -import { WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../shared/wallet/types' import { ETHAssetsTestnet } from '../../const' import { validAssetForETH } from '../../helpers/assetHelper' import { liveData } from '../../helpers/rx/liveData' @@ -30,17 +30,27 @@ const reloadBalances = () => { const balances$: ({ walletType, network, - walletIndex + walletIndex, + hdMode }: { walletType: WalletType network: Network walletIndex: number + hdMode: HDMode }) => C.WalletBalancesLD = ({ walletType, walletIndex, network }) => { // For testnet we limit requests by using pre-defined assets only // because `xchain-ethereum` does for each asset a single request const assets: Asset[] | undefined = network === 'testnet' ? ETHAssetsTestnet : undefined return FP.pipe( - C.balances$({ client$, trigger$: reloadBalances$, assets, walletType, walletIndex, walletBalanceType: 'all' }), + C.balances$({ + client$, + trigger$: reloadBalances$, + assets, + walletType, + walletIndex, + hdMode, + walletBalanceType: 'all' + }), // Filter assets based on ERC20Whitelist (mainnet only) liveData.map(FP.flow(A.filter(({ asset }) => validAssetForETH(asset, network)))) ) diff --git a/src/renderer/services/ethereum/transaction.ts b/src/renderer/services/ethereum/transaction.ts index b366013b1..e2ad29062 100644 --- a/src/renderer/services/ethereum/transaction.ts +++ b/src/renderer/services/ethereum/transaction.ts @@ -19,7 +19,7 @@ import { } from '../../../shared/api/io' import { LedgerError, Network } from '../../../shared/api/types' import { DEFAULT_APPROVE_GAS_LIMIT_FALLBACK } from '../../../shared/ethereum/const' -import { isError, isLedgerWallet } from '../../../shared/utils/guard' +import { isError, isEthHDMode, isLedgerWallet } from '../../../shared/utils/guard' import { addressInERC20Whitelist, getEthAssetAddress } from '../../helpers/assetHelper' import { sequenceSOption } from '../../helpers/fpHelpers' import { LiveData } from '../../helpers/rx/liveData' @@ -112,7 +112,8 @@ export const createTransactionService = (client$: Client$, network$: Network$): recipient: params.recipient, walletIndex: params.walletIndex, feeOption: params.feeOption, - nodeUrl: undefined + nodeUrl: undefined, + hdMode: params.hdMode } const encoded = ipcLedgerDepositTxParamsIO.encode(ipcParams) @@ -203,13 +204,24 @@ export const createTransactionService = (client$: Client$, network$: Network$): network, contractAddress, spenderAddress, - walletIndex + walletIndex, + hdMode }: ApproveParams): TxHashLD => { + if (!isEthHDMode(hdMode)) { + return Rx.of( + RD.failure({ + errorId: ErrorId.APPROVE_LEDGER_TX, + msg: `Invalid EthHDMode ${hdMode} - needed for Ledger to send ERC20 token.` + }) + ) + } + const ipcParams: IPCLedgerApproveERC20TokenParams = { network, contractAddress, spenderAddress, - walletIndex + walletIndex, + ethHdMode: hdMode } const encoded = ipcLedgerApproveERC20TokenParamsIO.encode(ipcParams) @@ -319,7 +331,8 @@ export const createTransactionService = (client$: Client$, network$: Network$): feeRate: NaN, feeOption: params.feeOption, feeAmount: undefined, - nodeUrl: undefined + nodeUrl: undefined, + hdMode: params.hdMode } const encoded = ipcLedgerSendTxParamsIO.encode(ipcParams) diff --git a/src/renderer/services/ethereum/types.ts b/src/renderer/services/ethereum/types.ts index b8b7891c9..7fc388aed 100644 --- a/src/renderer/services/ethereum/types.ts +++ b/src/renderer/services/ethereum/types.ts @@ -6,7 +6,7 @@ import { ethers } from 'ethers' import * as O from 'fp-ts/lib/Option' import { Network } from '../../../shared/api/types' -import { WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../shared/wallet/types' import { LiveData } from '../../helpers/rx/liveData' import { FeeLD, FeesLD, Memo } from '../chain/types' import * as C from '../clients' @@ -34,6 +34,7 @@ export type SendTxParams = { feeOption: FeeOption walletIndex: number walletType: WalletType + hdMode: HDMode } export type SendPoolTxParams = SendTxParams & { @@ -51,6 +52,7 @@ export type ApproveParams = { contractAddress: Address spenderAddress: Address fromAddress: Address // needed for estimating fees + hdMode: HDMode } export type IsApproveParams = { contractAddress: Address; spenderAddress: Address; fromAddress: Address } diff --git a/src/renderer/services/litecoin/balances.ts b/src/renderer/services/litecoin/balances.ts index e4d1ba034..eec5f26a2 100644 --- a/src/renderer/services/litecoin/balances.ts +++ b/src/renderer/services/litecoin/balances.ts @@ -1,4 +1,4 @@ -import { WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../shared/wallet/types' import { observableState } from '../../helpers/stateHelper' import * as C from '../clients' import { client$ } from './common' @@ -19,8 +19,16 @@ const reloadBalances = () => { } // State of balances loaded by Client -const balances$ = (walletType: WalletType, walletIndex: number): C.WalletBalancesLD => - C.balances$({ client$, trigger$: reloadBalances$, walletType, walletIndex, walletBalanceType: 'all' }) +const balances$ = ({ + walletType, + walletIndex, + hdMode +}: { + walletType: WalletType + walletIndex: number + hdMode: HDMode +}): C.WalletBalancesLD => + C.balances$({ client$, trigger$: reloadBalances$, walletType, walletIndex, hdMode, walletBalanceType: 'all' }) // State of balances loaded by Client and Address const getBalanceByAddress$ = C.balancesByAddress$({ client$, trigger$: reloadBalances$, walletBalanceType: 'all' }) diff --git a/src/renderer/services/litecoin/transaction.ts b/src/renderer/services/litecoin/transaction.ts index b9c707b3d..b2af6c6c8 100644 --- a/src/renderer/services/litecoin/transaction.ts +++ b/src/renderer/services/litecoin/transaction.ts @@ -33,7 +33,8 @@ export const createTransactionService = (client$: Client$, network$: Network$): recipient, memo, walletIndex, - nodeUrl: undefined + nodeUrl: undefined, + hdMode: 'default' } const encoded = ipcLedgerSendTxParamsIO.encode(sendLedgerTxParams) diff --git a/src/renderer/services/thorchain/balances.ts b/src/renderer/services/thorchain/balances.ts index 5b4230853..9918de2e1 100644 --- a/src/renderer/services/thorchain/balances.ts +++ b/src/renderer/services/thorchain/balances.ts @@ -1,6 +1,6 @@ import { AssetRuneNative } from '@xchainjs/xchain-util' -import { WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../shared/wallet/types' import { observableState } from '../../helpers/stateHelper' import * as C from '../clients' import { client$ } from './common' @@ -22,12 +22,21 @@ const reloadBalances = () => { // State of balances loaded by Client // TODO (@veado) Remove `assets` list to enable synths - currently we support `AssetRuneNative` only -const balances$ = (walletType: WalletType, walletIndex: number): C.WalletBalancesLD => +const balances$ = ({ + walletType, + walletIndex, + hdMode +}: { + walletType: WalletType + walletIndex: number + hdMode: HDMode +}): C.WalletBalancesLD => C.balances$({ client$, trigger$: reloadBalances$, walletType, walletIndex, + hdMode, assets: [AssetRuneNative], walletBalanceType: 'all' }) diff --git a/src/renderer/services/thorchain/transaction.ts b/src/renderer/services/thorchain/transaction.ts index 1170cef34..3aa849dc7 100644 --- a/src/renderer/services/thorchain/transaction.ts +++ b/src/renderer/services/thorchain/transaction.ts @@ -50,7 +50,8 @@ export const createTransactionService = ( router: undefined, walletIndex: params.walletIndex, feeOption: undefined, - nodeUrl: clientUrl[network].node + nodeUrl: clientUrl[network].node, + hdMode: 'default' } const encoded = ipcLedgerDepositTxParamsIO.encode(depositLedgerTxParams) @@ -145,7 +146,8 @@ export const createTransactionService = ( feeRate: NaN, feeOption: undefined, feeAmount: undefined, - nodeUrl: clientUrl[network].node + nodeUrl: clientUrl[network].node, + hdMode: 'default' } const encoded = ipcLedgerSendTxParamsIO.encode(sendLedgerTxParams) diff --git a/src/renderer/services/wallet/balances.ts b/src/renderer/services/wallet/balances.ts index 89ae78bc9..d6c29e065 100644 --- a/src/renderer/services/wallet/balances.ts +++ b/src/renderer/services/wallet/balances.ts @@ -22,7 +22,7 @@ import * as Rx from 'rxjs' import * as RxOp from 'rxjs/operators' import { Network } from '../../../shared/api/types' -import { WalletAddress, WalletBalanceType, WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletAddress, WalletBalanceType, WalletType } from '../../../shared/wallet/types' import { getBnbRuneAsset } from '../../helpers/assetHelper' import { filterEnabledChains } from '../../helpers/chainHelper' import { eqBalancesRD } from '../../helpers/fp/eq' @@ -106,11 +106,13 @@ export const createBalancesService = ({ chain, walletType, walletIndex, + hdMode, walletBalanceType }: { chain: Chain walletType: WalletType walletIndex: number + hdMode: HDMode walletBalanceType: WalletBalanceType }): ChainBalancesService => { switch (chain) { @@ -128,14 +130,14 @@ export const createBalancesService = ({ return { reloadBalances: BTC.reloadBalances, resetReloadBalances: BTC.resetReloadBalances, - balances$: BTC.balances$(walletType, walletIndex, walletBalanceType), + balances$: BTC.balances$({ walletType, walletIndex, walletBalanceType, hdMode }), reloadBalances$: BTC.reloadBalances$ } case BCHChain: return { reloadBalances: BCH.reloadBalances, resetReloadBalances: BCH.resetReloadBalances, - balances$: BCH.balances$(walletType, walletIndex), + balances$: BCH.balances$({ walletType, walletIndex, hdMode }), reloadBalances$: BCH.reloadBalances$ } case ETHChain: @@ -144,7 +146,7 @@ export const createBalancesService = ({ resetReloadBalances: ETH.resetReloadBalances, balances$: FP.pipe( network$, - RxOp.switchMap((network) => ETH.balances$({ walletType, network, walletIndex })) + RxOp.switchMap((network) => ETH.balances$({ walletType, network, walletIndex, hdMode })) ), reloadBalances$: ETH.reloadBalances$ } @@ -152,28 +154,28 @@ export const createBalancesService = ({ return { reloadBalances: THOR.reloadBalances, resetReloadBalances: THOR.resetReloadBalances, - balances$: THOR.balances$(walletType, walletIndex), + balances$: THOR.balances$({ walletType, walletIndex, hdMode }), reloadBalances$: THOR.reloadBalances$ } case LTCChain: return { reloadBalances: LTC.reloadBalances, resetReloadBalances: LTC.resetReloadBalances, - balances$: LTC.balances$(walletType, walletIndex), + balances$: LTC.balances$({ walletType, walletIndex, hdMode }), reloadBalances$: LTC.reloadBalances$ } case DOGEChain: return { reloadBalances: DOGE.reloadBalances, resetReloadBalances: DOGE.resetReloadBalances, - balances$: DOGE.balances$(walletType, walletIndex), + balances$: DOGE.balances$({ walletType, walletIndex, hdMode }), reloadBalances$: DOGE.reloadBalances$ } case CosmosChain: return { reloadBalances: COSMOS.reloadBalances, resetReloadBalances: COSMOS.resetReloadBalances, - balances$: COSMOS.balances$(walletType, walletIndex), + balances$: COSMOS.balances$({ walletType, walletIndex, hdMode }), reloadBalances$: COSMOS.reloadBalances$ } default: @@ -212,14 +214,16 @@ export const createBalancesService = ({ chain, walletType, walletIndex, + hdMode, walletBalanceType }: { chain: Chain walletType: WalletType walletIndex: number + hdMode: HDMode walletBalanceType: WalletBalanceType }): WalletBalancesLD => { - const chainService = getBalancesServiceByChain({ chain, walletType, walletIndex, walletBalanceType }) + const chainService = getBalancesServiceByChain({ chain, walletType, walletIndex, hdMode, walletBalanceType }) const reload$ = FP.pipe( chainService.reloadBalances$, RxOp.finalize(() => { @@ -260,7 +264,13 @@ export const createBalancesService = ({ */ const thorChainBalance$: ChainBalance$ = Rx.combineLatest([ THOR.addressUI$, - getChainBalance$({ chain: THORChain, walletType: 'keystore', walletIndex: 0, walletBalanceType: 'all' }) // walletIndex=0 (as long as we don't support HD wallets for keystore) + getChainBalance$({ + chain: THORChain, + walletType: 'keystore', + walletIndex: 0, // walletIndex=0 (as long as we don't support HD wallets for keystore) + hdMode: 'default', + walletBalanceType: 'all' + }) ]).pipe( RxOp.map(([oWalletAddress, balances]) => ({ walletType: 'keystore', @@ -286,11 +296,13 @@ export const createBalancesService = ({ address, walletType, walletIndex, + hdMode, walletBalanceType }: { address: Address walletType: WalletType walletIndex: number + hdMode: HDMode walletBalanceType: WalletBalanceType }) => WalletBalancesLD }): ChainBalance$ => @@ -311,11 +323,11 @@ export const createBalancesService = ({ walletIndex: 0, balancesType: walletBalanceType }), - ({ address, walletIndex }) => + ({ address, walletIndex, hdMode }) => // Load balances by given Ledger address // and put it's RD state into `balances` of `ChainBalance` FP.pipe( - getBalanceByAddress$({ address, walletType: 'ledger', walletIndex, walletBalanceType }), + getBalanceByAddress$({ address, walletType: 'ledger', walletIndex, walletBalanceType, hdMode }), RxOp.map((balances) => ({ walletType: 'ledger', walletIndex, @@ -344,7 +356,13 @@ export const createBalancesService = ({ */ const ltcBalance$: ChainBalance$ = Rx.combineLatest([ LTC.addressUI$, - getChainBalance$({ chain: LTCChain, walletType: 'keystore', walletIndex: 0, walletBalanceType: 'all' }) // walletIndex=0 (as long as we don't support HD wallets for keystore) + getChainBalance$({ + chain: LTCChain, + walletType: 'keystore', + walletIndex: 0, + hdMode: 'default', + walletBalanceType: 'all' + }) // walletIndex=0 (as long as we don't support HD wallets for keystore) ]).pipe( RxOp.map<[O.Option, WalletBalancesRD], ChainBalance>(([oWalletAddress, balances]) => ({ walletType: 'keystore', @@ -370,7 +388,13 @@ export const createBalancesService = ({ */ const bchChainBalance$: ChainBalance$ = Rx.combineLatest([ BCH.addressUI$, - getChainBalance$({ chain: BCHChain, walletType: 'keystore', walletIndex: 0, walletBalanceType: 'all' }) // walletIndex=0 (as long as we don't support HD wallets for keystore) + getChainBalance$({ + chain: BCHChain, + walletType: 'keystore', + walletIndex: 0, // walletIndex=0 (as long as we don't support HD wallets for keystore) + walletBalanceType: 'all', + hdMode: 'default' + }) ]).pipe( RxOp.map<[O.Option, WalletBalancesRD], ChainBalance>(([oWalletAddress, balances]) => ({ walletType: 'keystore', @@ -405,7 +429,13 @@ export const createBalancesService = ({ */ const bnbChainBalance$: ChainBalance$ = Rx.combineLatest([ BNB.addressUI$, - getChainBalance$({ chain: BNBChain, walletType: 'keystore', walletIndex: 0, walletBalanceType: 'all' }), // walletIndex=0 (as long as we don't support HD wallets for keystore) + getChainBalance$({ + chain: BNBChain, + walletType: 'keystore', + walletIndex: 0, // walletIndex=0 (as long as we don't support HD wallets for keystore) + hdMode: 'default', + walletBalanceType: 'all' + }), network$ ]).pipe( RxOp.map<[O.Option, WalletBalancesRD, Network], ChainBalance>( @@ -414,6 +444,7 @@ export const createBalancesService = ({ chain: BNBChain, walletAddress: addressFromOptionalWalletAddress(oWalletAddress), walletIndex: 0, // Always 0 as long as we don't support HD wallets for keystore + hdMode: 'default', balances: FP.pipe( balances, RD.map((assets) => sortBalances(assets, [AssetBNB.ticker, getBnbRuneAsset(network).ticker])) @@ -445,7 +476,13 @@ export const createBalancesService = ({ */ const btcChainBalance$: ChainBalance$ = Rx.combineLatest([ BTC.addressUI$, - getChainBalance$({ chain: BTCChain, walletType: 'keystore', walletIndex: 0, walletBalanceType: 'all' }) // walletIndex=0 (as long as we don't support HD wallets for keystore) + getChainBalance$({ + chain: BTCChain, + walletType: 'keystore', + walletIndex: 0, // walletIndex=0 (as long as we don't support HD wallets for keystore) + hdMode: 'default', + walletBalanceType: 'all' + }) ]).pipe( RxOp.map<[O.Option, WalletBalancesRD], ChainBalance>(([oWalletAddress, balances]) => ({ walletType: 'keystore', @@ -461,7 +498,13 @@ export const createBalancesService = ({ */ const btcChainBalanceConfirmed$: ChainBalance$ = Rx.combineLatest([ BTC.addressUI$, - getChainBalance$({ chain: BTCChain, walletType: 'keystore', walletIndex: 0, walletBalanceType: 'confirmed' }) // walletIndex=0 (as long as we don't support HD wallets for keystore) + getChainBalance$({ + chain: BTCChain, + walletType: 'keystore', + walletIndex: 0, // walletIndex=0 (as long as we don't support HD wallets for keystore) + hdMode: 'default', + walletBalanceType: 'confirmed' + }) ]).pipe( RxOp.map<[O.Option, WalletBalancesRD], ChainBalance>(([oWalletAddress, balances]) => ({ walletType: 'keystore', @@ -478,7 +521,13 @@ export const createBalancesService = ({ */ const dogeChainBalance$: ChainBalance$ = Rx.combineLatest([ DOGE.addressUI$, - getChainBalance$({ chain: DOGEChain, walletType: 'keystore', walletIndex: 0, walletBalanceType: 'all' }) // walletIndex=0 (as long as we don't support HD wallets for keystore) + getChainBalance$({ + chain: DOGEChain, + walletType: 'keystore', + walletIndex: 0, // walletIndex=0 (as long as we don't support HD wallets for keystore) + hdMode: 'default', + walletBalanceType: 'all' + }) ]).pipe( RxOp.map<[O.Option, WalletBalancesRD], ChainBalance>(([oWalletAddress, balances]) => ({ walletType: 'keystore', @@ -502,8 +551,8 @@ export const createBalancesService = ({ const ethBalances$ = getChainBalance$({ chain: ETHChain, walletType: 'keystore', - // walletIndex=0 (as long as we don't support HD wallets for keystore) - walletIndex: 0, + walletIndex: 0, // walletIndex=0 (as long as we don't support HD wallets for keystore) + hdMode: 'default', walletBalanceType: 'all' }) @@ -526,7 +575,13 @@ export const createBalancesService = ({ */ const cosmosChainBalance$: ChainBalance$ = Rx.combineLatest([ COSMOS.addressUI$, - getChainBalance$({ chain: CosmosChain, walletType: 'keystore', walletIndex: 0, walletBalanceType: 'all' }) // walletIndex=0 (as long as we don't support HD wallets for keystore) + getChainBalance$({ + chain: CosmosChain, + walletType: 'keystore', + walletIndex: 0, // walletIndex=0 (as long as we don't support HD wallets for keystore) + hdMode: 'default', + walletBalanceType: 'all' + }) ]).pipe( RxOp.map<[O.Option, WalletBalancesRD], ChainBalance>(([oWalletAddress, balances]) => ({ walletType: 'keystore', diff --git a/src/renderer/services/wallet/common.ts b/src/renderer/services/wallet/common.ts index 4cdd47fd6..14a01173a 100644 --- a/src/renderer/services/wallet/common.ts +++ b/src/renderer/services/wallet/common.ts @@ -1,21 +1,24 @@ -import { Asset } from '@xchainjs/xchain-util' import * as O from 'fp-ts/lib/Option' import * as RxOp from 'rxjs/operators' import { distinctUntilChanged } from 'rxjs/operators' -import { eqOAsset, eqOChain } from '../../helpers/fp/eq' +import { eqOChain, eqOSelectedWalletAsset } from '../../helpers/fp/eq' import { observableState } from '../../helpers/stateHelper' import { getClientByChain$ } from '../chain/client' +import { SelectedWalletAsset } from './types' -const { get$: getSelectedAsset$, set: setSelectedAsset } = observableState>(O.none) +const { get$: getSelectedAsset$, set: setSelectedAsset } = observableState>(O.none) // "dirty check" to trigger "real" changes of an asset only -const selectedAsset$ = getSelectedAsset$.pipe(distinctUntilChanged(eqOAsset.equals)) +const selectedAsset$ = getSelectedAsset$.pipe(distinctUntilChanged(eqOSelectedWalletAsset.equals)) /** * Selected chain depending on selected asset */ -const selectedChain$ = selectedAsset$.pipe(RxOp.map(O.map(({ chain }) => chain)), distinctUntilChanged(eqOChain.equals)) +const selectedChain$ = selectedAsset$.pipe( + RxOp.map(O.map(({ asset }) => asset.chain)), + distinctUntilChanged(eqOChain.equals) +) /** * Wallet client depends on selected chain diff --git a/src/renderer/services/wallet/ledger.ts b/src/renderer/services/wallet/ledger.ts index e671c8d37..7164d194a 100644 --- a/src/renderer/services/wallet/ledger.ts +++ b/src/renderer/services/wallet/ledger.ts @@ -143,14 +143,14 @@ export const createLedgerService = ({ ) ) - const verifyLedgerAddress$: VerifyLedgerAddressHandler = ({ chain, network, walletIndex, ethDerivationMode }) => + const verifyLedgerAddress$: VerifyLedgerAddressHandler = ({ chain, network, walletIndex, hdMode }) => FP.pipe( Rx.from( window.apiHDWallet.verifyLedgerAddress({ chain, network, walletIndex, - ethDerivationMode: O.toUndefined(ethDerivationMode) + hdMode }) ), RxOp.catchError((error: Error) => Rx.of(RD.failure(error))), @@ -176,14 +176,14 @@ export const createLedgerService = ({ /** * Add Ledger by asking address from it */ - const addLedgerAddress$: AddLedgerAddressHandler = ({ id, chain, network, ethDerivationMode, walletIndex }) => + const addLedgerAddress$: AddLedgerAddressHandler = ({ id, chain, network, hdMode, walletIndex }) => FP.pipe( Rx.from( window.apiHDWallet.getLedgerAddress({ chain, network, walletIndex, - ethDerivationMode: O.toUndefined(ethDerivationMode) + hdMode }) ), RxOp.map(RD.fromEither), @@ -200,9 +200,10 @@ export const createLedgerService = ({ keystoreId: id, chain, network, - ethDerivationMode, + hdMode, walletIndex, - address + address, + type: 'ledger' } // store address in memory _addLedgerAddress(ledgerAddress) diff --git a/src/renderer/services/wallet/transaction.ts b/src/renderer/services/wallet/transaction.ts index 6b4b7e43f..9ca355b31 100644 --- a/src/renderer/services/wallet/transaction.ts +++ b/src/renderer/services/wallet/transaction.ts @@ -55,7 +55,7 @@ export const getTxs$: (walletAddress: O.Option, walletIndex: number) => oAsset, O.fold( () => Rx.of(RD.initial), - (asset) => { + ({ asset }) => { switch (asset.chain) { case BNBChain: return BNB.txs$({ asset: O.some(asset), limit, offset, walletAddress, walletIndex }) diff --git a/src/renderer/services/wallet/types.ts b/src/renderer/services/wallet/types.ts index ca03e8fb3..5a1b99a11 100644 --- a/src/renderer/services/wallet/types.ts +++ b/src/renderer/services/wallet/types.ts @@ -1,7 +1,7 @@ import * as RD from '@devexperts/remote-data-ts' import { Address, Balance, Tx, TxHash } from '@xchainjs/xchain-client' import { Keystore } from '@xchainjs/xchain-crypto' -import { Chain } from '@xchainjs/xchain-util' +import { Asset, Chain } from '@xchainjs/xchain-util' import { getMonoid } from 'fp-ts/Array' import * as FP from 'fp-ts/lib/function' import { NonEmptyArray } from 'fp-ts/lib/NonEmptyArray' @@ -10,8 +10,7 @@ import * as Rx from 'rxjs' import { KeystoreWallet, KeystoreWallets } from '../../../shared/api/io' import { KeystoreId, LedgerError, Network } from '../../../shared/api/types' -import { EthDerivationMode } from '../../../shared/ethereum/types' -import { WalletAddress, WalletBalanceType, WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletAddress, WalletBalanceType, WalletType } from '../../../shared/wallet/types' import { LiveData } from '../../helpers/rx/liveData' import { LoadTxsParams, WalletBalancesLD, WalletBalancesRD } from '../clients' @@ -99,9 +98,21 @@ export type WalletAccount = { export type WalletAccounts = WalletAccount[] -export type WalletBalance = Balance & { walletAddress: Address; walletType: WalletType; walletIndex: number } +export type WalletBalance = Balance & { + walletAddress: Address + walletType: WalletType + walletIndex: number + hdMode: HDMode +} export type WalletBalances = WalletBalance[] +export type SelectedWalletAsset = { + asset: Asset + walletAddress: Address + walletType: WalletType + walletIndex: number + hdMode: HDMode +} /** * Wraps WalletBalancesRD into an object to provide extra information (`Address` + `Chain` + `WalletType`) * Currently needed in `AssetView` - TODO(@Veado) Think about to extract it into view layer (as helper or so) @@ -109,6 +120,7 @@ export type WalletBalances = WalletBalance[] export type ChainBalance = { walletType: WalletType walletIndex: number + hdMode: HDMode walletAddress: O.Option
chain: Chain balances: WalletBalancesRD @@ -184,12 +196,12 @@ export type VerifyLedgerAddressHandler = ({ chain, network, walletIndex, - ethDerivationMode + hdMode }: { chain: Chain network: Network walletIndex: number - ethDerivationMode: O.Option + hdMode: HDMode }) => VerifiedLedgerAddressLD export type AddLedgerAddressHandler = ({ @@ -197,13 +209,13 @@ export type AddLedgerAddressHandler = ({ chain, network, walletIndex, - ethDerivationMode + hdMode }: { id: KeystoreId chain: Chain network: Network walletIndex: number - ethDerivationMode: O.Option + hdMode: HDMode }) => LedgerAddressLD export type RemoveLedgerAddressHandler = ({ @@ -245,9 +257,9 @@ export type LedgerTxHashRD = RD.RemoteData export type LedgerTxHashLD = LiveData export type LedgerAddress = Omit & { - keystoreId: KeystoreId - network: Network - ethDerivationMode: O.Option + readonly keystoreId: KeystoreId + readonly network: Network + readonly type: 'ledger' } export type LedgerAddressRD = RD.RemoteData export type LedgerAddressLD = LiveData diff --git a/src/renderer/services/wallet/util.test.ts b/src/renderer/services/wallet/util.test.ts index 107cebae9..f15579c3e 100644 --- a/src/renderer/services/wallet/util.test.ts +++ b/src/renderer/services/wallet/util.test.ts @@ -135,35 +135,40 @@ describe('services/wallet/util/', () => { amount: baseAmount(0), walletAddress: 'ADDRESS_TOMO', walletIndex: 0, - walletType: 'keystore' + walletType: 'keystore', + hdMode: 'default' }, { asset: ASSETS_TESTNET.BOLT, amount: baseAmount(1), walletAddress: 'ADDRESS_BOLT', walletIndex: 0, - walletType: 'keystore' + walletType: 'keystore', + hdMode: 'default' }, { asset: ASSETS_TESTNET.FTM, amount: baseAmount(0), walletAddress: 'ADDRESS_FTM', walletIndex: 0, - walletType: 'keystore' + walletType: 'keystore', + hdMode: 'default' }, { asset: AssetBNB, amount: baseAmount(2), walletAddress: 'ADDRESS_BNB', walletIndex: 0, - walletType: 'keystore' + walletType: 'keystore', + hdMode: 'default' }, { asset: AssetRuneNative, amount: baseAmount(0), walletAddress: 'ADDRESS_RUNENATIVE', walletIndex: 0, - walletType: 'keystore' + walletType: 'keystore', + hdMode: 'default' } ] const nullableBalances = filterNullableBalances(target) @@ -179,35 +184,40 @@ describe('services/wallet/util/', () => { amount: baseAmount(0), walletAddress: 'ADDRESS_TOMO', walletIndex: 0, - walletType: 'keystore' + walletType: 'keystore', + hdMode: 'default' }, { asset: ASSETS_TESTNET.BOLT, amount: baseAmount(1), walletAddress: 'ADDRESS_BOLT', walletIndex: 0, - walletType: 'keystore' + walletType: 'keystore', + hdMode: 'default' }, { asset: ASSETS_TESTNET.FTM, amount: baseAmount(0), walletAddress: 'ADDRESS_FTM', walletIndex: 0, - walletType: 'keystore' + walletType: 'keystore', + hdMode: 'default' }, { asset: AssetBNB, amount: baseAmount(2), walletAddress: 'ADDRESS_BNB', walletIndex: 0, - walletType: 'keystore' + walletType: 'keystore', + hdMode: 'default' }, { asset: AssetRuneNative, amount: baseAmount(0), walletAddress: 'ADDRESS_RUNENATIVE', walletIndex: 0, - walletType: 'keystore' + walletType: 'keystore', + hdMode: 'default' } ] const balances = sortBalances(target, [AssetBTC.ticker, AssetETH.ticker, AssetRuneNative.ticker, AssetBNB.ticker]) @@ -223,35 +233,40 @@ describe('services/wallet/util/', () => { amount: baseAmount(0), walletAddress: 'ADDRESS_TOMO', walletIndex: 0, - walletType: 'keystore' + walletType: 'keystore', + hdMode: 'default' }, { asset: ASSETS_TESTNET.BOLT, amount: baseAmount(1), walletAddress: 'ADDRESS_BOLT', walletIndex: 0, - walletType: 'keystore' + walletType: 'keystore', + hdMode: 'default' }, { asset: ASSETS_TESTNET.FTM, amount: baseAmount(0), walletAddress: 'ADDRESS_FTM', walletIndex: 0, - walletType: 'keystore' + walletType: 'keystore', + hdMode: 'default' }, { asset: AssetBNB, amount: baseAmount(2), walletAddress: 'ADDRESS_BNB', walletIndex: 0, - walletType: 'keystore' + walletType: 'keystore', + hdMode: 'default' }, { asset: AssetRuneNative, amount: baseAmount(0), walletAddress: 'ADDRESS_RUNENATIVE', walletIndex: 0, - walletType: 'keystore' + walletType: 'keystore', + hdMode: 'default' } ] @@ -264,7 +279,8 @@ describe('services/wallet/util/', () => { amount: baseAmount(2), walletAddress: 'ADDRESS_BNB', walletIndex: 0, - walletType: 'keystore' + walletType: 'keystore', + hdMode: 'default' }) ) ).toBeTruthy() diff --git a/src/renderer/services/wallet/util.ts b/src/renderer/services/wallet/util.ts index ece148c2c..7371e03aa 100644 --- a/src/renderer/services/wallet/util.ts +++ b/src/renderer/services/wallet/util.ts @@ -204,35 +204,46 @@ export const ledgerErrorIdToI18n = (errorId: LedgerErrorId, intl: IntlShape) => } } -export const ledgerAddressToWalletAddress = ({ walletIndex, address, chain }: LedgerAddress): WalletAddress => ({ - type: 'ledger', +/** + * Type transformation + */ +export const ledgerAddressToWalletAddress = ({ + walletIndex, + address, + chain, + type, + hdMode +}: LedgerAddress): WalletAddress => ({ + type, walletIndex, address, - chain + chain, + hdMode }) export const toIPCLedgerAddressesIO = (addresses: LedgerAddresses): IPCLedgerAddressesIO => FP.pipe( addresses, - A.map(({ keystoreId, address, chain, network, walletIndex, ethDerivationMode }) => ({ + A.map(({ keystoreId, address, chain, network, walletIndex, hdMode }) => ({ keystoreId, address, chain, network, walletIndex, - ethDerivationMode: O.toUndefined(ethDerivationMode) + hdMode })) ) export const fromIPCLedgerAddressesIO = (addresses: IPCLedgerAddressesIO): LedgerAddresses => FP.pipe( addresses, - A.map(({ keystoreId, address, chain, network, walletIndex, ethDerivationMode }) => ({ + A.map(({ keystoreId, address, chain, network, walletIndex, hdMode }) => ({ keystoreId, address, chain, network, walletIndex, - ethDerivationMode: O.fromNullable(ethDerivationMode) + hdMode, + type: 'ledger' })) ) diff --git a/src/renderer/views/wallet/AssetDetailsView.tsx b/src/renderer/views/wallet/AssetDetailsView.tsx index 338caafbd..ed9d49f9d 100644 --- a/src/renderer/views/wallet/AssetDetailsView.tsx +++ b/src/renderer/views/wallet/AssetDetailsView.tsx @@ -6,73 +6,40 @@ import * as FP from 'fp-ts/function' import * as O from 'fp-ts/lib/Option' import * as NEA from 'fp-ts/NonEmptyArray' import { useObservableState } from 'observable-hooks' -import { useIntl } from 'react-intl' -import { useParams } from 'react-router-dom' import * as Rx from 'rxjs' -import { ErrorView } from '../../components/shared/error' +import { LoadingView } from '../../components/shared/loading' import { BackLink } from '../../components/uielements/backLink' import { AssetDetails } from '../../components/wallet/assets' import { useChainContext } from '../../contexts/ChainContext' import { useWalletContext } from '../../contexts/WalletContext' -import { disableRuneUpgrade, getAssetFromNullableString, isRuneNativeAsset } from '../../helpers/assetHelper' +import { disableRuneUpgrade, isRuneNativeAsset } from '../../helpers/assetHelper' import { sequenceTOption } from '../../helpers/fpHelpers' -import { - getWalletAddressFromNullableString, - getWalletIndexFromNullableString, - getWalletTypeFromNullableString -} from '../../helpers/walletHelper' import { useMimirHalt } from '../../hooks/useMimirHalt' import { useNetwork } from '../../hooks/useNetwork' import { useOpenExplorerTxUrl } from '../../hooks/useOpenExplorerTxUrl' -import { AssetDetailsParams } from '../../routes/wallet' import { DEFAULT_BALANCES_FILTER, INITIAL_BALANCES_STATE } from '../../services/wallet/const' -import { ErrorId } from '../../services/wallet/types' export const AssetDetailsView: React.FC = (): JSX.Element => { - const intl = useIntl() - - const { - asset: routeAsset, - walletAddress: routeWalletAddress, - walletIndex: routeWalletIndex, - walletType: routeWalletType - } = useParams() - - const oAsset = getAssetFromNullableString(routeAsset) - const oWalletAddress = getWalletAddressFromNullableString(routeWalletAddress) - const oWalletIndex = getWalletIndexFromNullableString(routeWalletIndex) - const oWalletType = getWalletTypeFromNullableString(routeWalletType) - const { clientByChain$ } = useChainContext() const { mimirHalt: { haltThorChain, haltEthChain, haltBnbChain } } = useMimirHalt() - const { getTxs$, balancesState$, loadTxs, reloadBalancesByChain, setSelectedAsset, resetTxsPage } = useWalletContext() + const { getTxs$, balancesState$, loadTxs, reloadBalancesByChain, selectedAsset$, resetTxsPage } = useWalletContext() + + const oSelectedAsset = useObservableState(selectedAsset$, O.none) const [txsRD] = useObservableState( () => FP.pipe( - oWalletIndex, + oSelectedAsset, O.fold( - () => - Rx.of( - RD.failure({ - errorId: ErrorId.GET_ASSET_TXS, - msg: intl.formatMessage( - { id: 'routes.invalid.params' }, - { - params: `walletIndex: ${routeWalletIndex}` - } - ) - }) - ), - (walletIndex) => getTxs$(oWalletAddress, walletIndex) + () => Rx.of(RD.pending), + ({ walletIndex, walletAddress }) => getTxs$(O.some(walletAddress), walletIndex) ) ), - RD.initial ) @@ -81,13 +48,6 @@ export const AssetDetailsView: React.FC = (): JSX.Element => { INITIAL_BALANCES_STATE ) - // Set selected asset once - // Needed to get all data for this asset (transactions etc.) - useEffect(() => { - setSelectedAsset(oAsset) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) - useEffect(() => { return () => resetTxsPage() @@ -102,41 +62,24 @@ export const AssetDetailsView: React.FC = (): JSX.Element => { const walletBalances = useMemo( () => FP.pipe( - sequenceTOption(oBalances, oWalletAddress), - O.map(([balances, walletAddress]) => + sequenceTOption(oBalances, oSelectedAsset), + O.map(([balances, { walletAddress }]) => balances.filter((walletBalance) => walletBalance.walletAddress === walletAddress) ), O.chain(NEA.fromArray) ), - [oBalances, oWalletAddress] + [oBalances, oSelectedAsset] ) const { network } = useNetwork() - const renderRouteError = useMemo( - () => ( - <> - - - - ), - [intl, routeAsset, routeWalletAddress, routeWalletIndex, routeWalletType] - ) - const [oClient] = useObservableState>( () => FP.pipe( - oAsset, + oSelectedAsset, O.fold( () => Rx.of(O.none), - (asset) => clientByChain$(asset.chain) + ({ asset }) => clientByChain$(asset.chain) ) ), O.none @@ -144,32 +87,32 @@ export const AssetDetailsView: React.FC = (): JSX.Element => { const openExplorerAddressUrlHandler = useCallback(() => { FP.pipe( - sequenceTOption(oClient, oWalletAddress), - O.map(async ([client, address]) => { - const url = client.getExplorerAddressUrl(address) + sequenceTOption(oClient, oSelectedAsset), + O.map(async ([client, { walletAddress }]) => { + const url = client.getExplorerAddressUrl(walletAddress) await window.apiUrl.openExternal(url) return true }) ) - }, [oClient, oWalletAddress]) + }, [oClient, oSelectedAsset]) const { openExplorerTxUrl } = useOpenExplorerTxUrl( FP.pipe( - oAsset, - O.map(({ chain }) => chain) + oSelectedAsset, + O.map(({ asset }) => asset.chain) ) ) return ( <> + {FP.pipe( - sequenceTOption(oAsset, oWalletAddress, oWalletIndex, oWalletType), + oSelectedAsset, O.fold( - () => renderRouteError, - ([asset, walletAddress, walletIndex, walletType]) => ( + () => , + ({ asset, walletAddress, walletType }) => ( { const navigate = useNavigate() @@ -74,26 +72,19 @@ export const AssetsView: React.FC = (): JSX.Element => { const selectedPricePool = useObservableState(selectedPricePool$, RUNE_PRICE_POOL) const selectAssetHandler = useCallback( - ({ - asset, - walletAddress, - walletType, - walletIndex - }: { - asset: Asset - walletAddress: Address - walletType: WalletType - walletIndex: number - }) => - navigate( - walletRoutes.assetDetail.path({ - asset: assetToString(asset), - walletAddress, - walletType, - walletIndex: walletIndex.toString() - }) - ), - [navigate] + (selectedAsset: SelectedWalletAsset) => { + setSelectedAsset(O.some(selectedAsset)) + navigate(walletRoutes.assetDetail.path()) + }, + [navigate, setSelectedAsset] + ) + + const upgradeAssetHandler = useCallback( + (selectedAsset: SelectedWalletAsset) => { + setSelectedAsset(O.some(selectedAsset)) + navigate(walletRoutes.upgradeRune.path()) + }, + [navigate, setSelectedAsset] ) const poolDetails = RD.toNullable(poolsRD)?.poolDetails ?? [] @@ -124,7 +115,7 @@ export const AssetsView: React.FC = (): JSX.Element => { pricePool={selectedPricePool} poolDetails={poolDetails} selectAssetHandler={selectAssetHandler} - setSelectedAsset={setSelectedAsset} + upgradeAssetHandler={upgradeAssetHandler} mimirHalt={mimirHaltRD} network={network} /> diff --git a/src/renderer/views/wallet/WalletSettingsView.tsx b/src/renderer/views/wallet/WalletSettingsView.tsx index 5214de1d5..db3a0f30e 100644 --- a/src/renderer/views/wallet/WalletSettingsView.tsx +++ b/src/renderer/views/wallet/WalletSettingsView.tsx @@ -21,7 +21,7 @@ import * as Rx from 'rxjs' import * as RxOp from 'rxjs/operators' import { LedgerErrorId } from '../../../shared/api/types' -import { EthDerivationMode } from '../../../shared/ethereum/types' +import { EthHDMode } from '../../../shared/ethereum/types' import { WalletSettings } from '../../components/settings' import { useBinanceContext } from '../../contexts/BinanceContext' import { useBitcoinCashContext } from '../../contexts/BitcoinCashContext' @@ -144,7 +144,7 @@ export const WalletSettingsView: React.FC = ({ keystoreUnlocked }): JSX.E }: { chain: Chain walletIndex: number - ethDerivationMode: O.Option + ethDerivationMode: O.Option }): LedgerAddressLD => { if (isThorChain(chain)) return addLedgerThorAddress(walletIndex, ethDerivationMode) if (isBnbChain(chain)) return addLedgerBnbAddress(walletIndex, ethDerivationMode) @@ -170,7 +170,7 @@ export const WalletSettingsView: React.FC = ({ keystoreUnlocked }): JSX.E }: { chain: Chain walletIndex: number - ethDerivationMode: O.Option + ethDerivationMode: O.Option }): VerifiedLedgerAddressLD => { if (isThorChain(chain)) return verifyLedgerThorAddress(walletIndex, ethDerivationMode) if (isBnbChain(chain)) return verifyLedgerBnbAddress(walletIndex, ethDerivationMode) diff --git a/src/renderer/views/wallet/send/SendView.tsx b/src/renderer/views/wallet/send/SendView.tsx index 3b5b51310..6850fed5b 100644 --- a/src/renderer/views/wallet/send/SendView.tsx +++ b/src/renderer/views/wallet/send/SendView.tsx @@ -1,9 +1,6 @@ -import React, { useCallback, useMemo } from 'react' +import React, { useCallback } from 'react' -import { Address } from '@xchainjs/xchain-client' import { - Asset, - assetToString, BCHChain, BNBChain, BTCChain, @@ -15,15 +12,14 @@ import { } from '@xchainjs/xchain-util' import * as FP from 'fp-ts/lib/function' import * as O from 'fp-ts/Option' +import { useObservableState } from 'observable-hooks' import { useIntl } from 'react-intl' -import { useParams } from 'react-router-dom' -import { WalletType } from '../../../../shared/wallet/types' -import { ErrorView } from '../../../components/shared/error/' +import { LoadingView } from '../../../components/shared/loading' import { BackLink } from '../../../components/uielements/backLink' -import { getAssetWalletParams } from '../../../helpers/routeHelper' -import { SendParams } from '../../../routes/wallet' +import { useWalletContext } from '../../../contexts/WalletContext' import * as walletRoutes from '../../../routes/wallet' +import { SelectedWalletAsset } from '../../../services/wallet/types' import { SendViewBNB, SendViewBCH, @@ -38,78 +34,41 @@ import { type Props = {} export const SendView: React.FC = (): JSX.Element => { - const routeParams = useParams() - const oRouteParams = getAssetWalletParams(routeParams) - const intl = useIntl() - const renderRouteError = useMemo(() => { - const { asset, walletAddress, walletType, walletIndex } = routeParams - return ( - <> - - - - ) - }, [intl, routeParams]) + const { selectedAsset$ } = useWalletContext() + + const oSelectedAsset = useObservableState(selectedAsset$, O.none) const renderSendView = useCallback( - ({ - asset, - walletAddress, - walletType, - walletIndex - }: { - asset: Asset - walletAddress: Address - walletType: WalletType - walletIndex: number - }) => { - switch (asset.chain) { + (asset: SelectedWalletAsset) => { + const { + asset: { chain } + } = asset + switch (chain) { case BNBChain: - return ( - - ) + return case BCHChain: - return + return case BTCChain: - return + return case ETHChain: - return ( - - ) + return case THORChain: - return + return case LTCChain: - return + return case DOGEChain: - return + return case CosmosChain: - return + return default: return (

{intl.formatMessage( { id: 'wallet.errors.invalidChain' }, { - chain: asset.chain + chain } )}

@@ -120,20 +79,13 @@ export const SendView: React.FC = (): JSX.Element => { ) return FP.pipe( - oRouteParams, + oSelectedAsset, O.fold( - () => renderRouteError, - ({ asset, walletAddress, walletType, walletIndex }) => ( + () => , + (selectedAsset) => ( <> - - {renderSendView({ asset, walletAddress, walletType, walletIndex })} + + {renderSendView(selectedAsset)} ) ) diff --git a/src/renderer/views/wallet/send/SendViewBCH.tsx b/src/renderer/views/wallet/send/SendViewBCH.tsx index ae2e018fc..166cf6734 100644 --- a/src/renderer/views/wallet/send/SendViewBCH.tsx +++ b/src/renderer/views/wallet/send/SendViewBCH.tsx @@ -1,13 +1,11 @@ import React, { useMemo } from 'react' import * as RD from '@devexperts/remote-data-ts' -import { Address } from '@xchainjs/xchain-client' import { BCHChain } from '@xchainjs/xchain-util' import * as FP from 'fp-ts/lib/function' import * as O from 'fp-ts/Option' import { useObservableState } from 'observable-hooks' -import { WalletType } from '../../../../shared/wallet/types' import { LoadingView } from '../../../components/shared/loading' import { SendFormBCH } from '../../../components/wallet/txs/send' import { useBitcoinCashContext } from '../../../contexts/BitcoinCashContext' @@ -20,15 +18,14 @@ import { useValidateAddress } from '../../../hooks/useValidateAddress' import { FeesWithRatesLD } from '../../../services/bitcoincash/types' import { WalletBalances } from '../../../services/clients' import { DEFAULT_BALANCES_FILTER, INITIAL_BALANCES_STATE } from '../../../services/wallet/const' +import { SelectedWalletAsset } from '../../../services/wallet/types' type Props = { - walletType: WalletType - walletIndex: number - walletAddress: Address + asset: SelectedWalletAsset } export const SendViewBCH: React.FC = (props): JSX.Element => { - const { walletType, walletIndex, walletAddress } = props + const { asset } = props const { network } = useNetwork() @@ -48,9 +45,9 @@ export const SendViewBCH: React.FC = (props): JSX.Element => { () => FP.pipe( oBalances, - O.chain((balances) => getWalletBalanceByAddress(balances, walletAddress)) + O.chain((balances) => getWalletBalanceByAddress(balances, asset.walletAddress)) ), - [oBalances, walletAddress] + [asset.walletAddress, oBalances] ) const { transfer$ } = useChainContext() @@ -66,9 +63,7 @@ export const SendViewBCH: React.FC = (props): JSX.Element => { () => , (walletBalance) => ( (() => []) diff --git a/src/renderer/views/wallet/send/SendViewBNB.tsx b/src/renderer/views/wallet/send/SendViewBNB.tsx index f91d48fe1..a9be5f1aa 100644 --- a/src/renderer/views/wallet/send/SendViewBNB.tsx +++ b/src/renderer/views/wallet/send/SendViewBNB.tsx @@ -1,13 +1,11 @@ import React, { useMemo } from 'react' import * as RD from '@devexperts/remote-data-ts' -import { Address } from '@xchainjs/xchain-client' -import { Asset, BNBChain } from '@xchainjs/xchain-util' +import { BNBChain } from '@xchainjs/xchain-util' import * as FP from 'fp-ts/lib/function' import * as O from 'fp-ts/lib/Option' import { useObservableState } from 'observable-hooks' -import { WalletType } from '../../../../shared/wallet/types' import { LoadingView } from '../../../components/shared/loading' import { SendFormBNB } from '../../../components/wallet/txs/send' import { useBinanceContext } from '../../../contexts/BinanceContext' @@ -21,16 +19,14 @@ import { useValidateAddress } from '../../../hooks/useValidateAddress' import { FeeRD } from '../../../services/chain/types' import { WalletBalances } from '../../../services/clients' import { DEFAULT_BALANCES_FILTER, INITIAL_BALANCES_STATE } from '../../../services/wallet/const' +import { SelectedWalletAsset } from '../../../services/wallet/types' type Props = { - walletType: WalletType - walletAddress: Address - walletIndex: number - asset: Asset + asset: SelectedWalletAsset } export const SendViewBNB: React.FC = (props): JSX.Element => { - const { walletAddress, asset, walletType, walletIndex } = props + const { asset } = props const { network } = useNetwork() @@ -50,9 +46,11 @@ export const SendViewBNB: React.FC = (props): JSX.Element => { () => FP.pipe( oBalances, - O.chain((balances) => getWalletBalanceByAddressAndAsset({ balances, address: walletAddress, asset })) + O.chain((balances) => + getWalletBalanceByAddressAndAsset({ balances, address: asset.walletAddress, asset: asset.asset }) + ) ), - [asset, oBalances, walletAddress] + [asset, oBalances] ) const { transfer$ } = useChainContext() @@ -76,9 +74,7 @@ export const SendViewBNB: React.FC = (props): JSX.Element => { () => , (walletBalance) => ( (() => []) diff --git a/src/renderer/views/wallet/send/SendViewBTC.tsx b/src/renderer/views/wallet/send/SendViewBTC.tsx index 6f633f4ef..a77f2bdab 100644 --- a/src/renderer/views/wallet/send/SendViewBTC.tsx +++ b/src/renderer/views/wallet/send/SendViewBTC.tsx @@ -1,13 +1,11 @@ import React, { useMemo } from 'react' import * as RD from '@devexperts/remote-data-ts' -import { Address } from '@xchainjs/xchain-client' import { BTCChain } from '@xchainjs/xchain-util' import * as FP from 'fp-ts/lib/function' import * as O from 'fp-ts/Option' import { useObservableState } from 'observable-hooks' -import { WalletType } from '../../../../shared/wallet/types' import { LoadingView } from '../../../components/shared/loading' import { SendFormBTC } from '../../../components/wallet/txs/send/' import { useBitcoinContext } from '../../../contexts/BitcoinContext' @@ -20,15 +18,14 @@ import { useValidateAddress } from '../../../hooks/useValidateAddress' import { FeesWithRatesLD } from '../../../services/bitcoin/types' import { WalletBalances } from '../../../services/clients' import { DEFAULT_BALANCES_FILTER, INITIAL_BALANCES_STATE } from '../../../services/wallet/const' +import { SelectedWalletAsset } from '../../../services/wallet/types' type Props = { - walletType: WalletType - walletIndex: number - walletAddress: Address + asset: SelectedWalletAsset } export const SendViewBTC: React.FC = (props): JSX.Element => { - const { walletType, walletIndex, walletAddress } = props + const { asset } = props const { network } = useNetwork() @@ -48,9 +45,9 @@ export const SendViewBTC: React.FC = (props): JSX.Element => { () => FP.pipe( oBalances, - O.chain((balances) => getWalletBalanceByAddress(balances, walletAddress)) + O.chain((balances) => getWalletBalanceByAddress(balances, asset.walletAddress)) ), - [oBalances, walletAddress] + [asset.walletAddress, oBalances] ) const { transfer$ } = useChainContext() @@ -68,9 +65,7 @@ export const SendViewBTC: React.FC = (props): JSX.Element => { () => , (walletBalance) => ( (() => []) diff --git a/src/renderer/views/wallet/send/SendViewCOSMOS.tsx b/src/renderer/views/wallet/send/SendViewCOSMOS.tsx index abc197b5b..aa66337fb 100644 --- a/src/renderer/views/wallet/send/SendViewCOSMOS.tsx +++ b/src/renderer/views/wallet/send/SendViewCOSMOS.tsx @@ -1,13 +1,11 @@ import React, { useMemo } from 'react' import * as RD from '@devexperts/remote-data-ts' -import { Address } from '@xchainjs/xchain-client' import { CosmosChain } from '@xchainjs/xchain-util' import * as FP from 'fp-ts/lib/function' import * as O from 'fp-ts/Option' import { useObservableState } from 'observable-hooks' -import { WalletType } from '../../../../shared/wallet/types' import { LoadingView } from '../../../components/shared/loading' import { SendFormCOSMOS } from '../../../components/wallet/txs/send' import { useChainContext } from '../../../contexts/ChainContext' @@ -21,15 +19,14 @@ import { useValidateAddress } from '../../../hooks/useValidateAddress' import { FeeRD } from '../../../services/chain/types' import { WalletBalances } from '../../../services/clients' import { DEFAULT_BALANCES_FILTER, INITIAL_BALANCES_STATE } from '../../../services/wallet/const' +import { SelectedWalletAsset } from '../../../services/wallet/types' type Props = { - walletType: WalletType - walletIndex: number - walletAddress: Address + asset: SelectedWalletAsset } export const SendViewCOSMOS: React.FC = (props): JSX.Element => { - const { walletType, walletIndex, walletAddress } = props + const { asset } = props const { network } = useNetwork() const { @@ -48,9 +45,9 @@ export const SendViewCOSMOS: React.FC = (props): JSX.Element => { () => FP.pipe( oBalances, - O.chain((balances) => getWalletBalanceByAddress(balances, walletAddress)) + O.chain((balances) => getWalletBalanceByAddress(balances, asset.walletAddress)) ), - [oBalances, walletAddress] + [asset.walletAddress, oBalances] ) const { transfer$ } = useChainContext() @@ -74,8 +71,7 @@ export const SendViewCOSMOS: React.FC = (props): JSX.Element => { () => , (walletBalance) => ( (() => []) diff --git a/src/renderer/views/wallet/send/SendViewDOGE.tsx b/src/renderer/views/wallet/send/SendViewDOGE.tsx index 80d4f3966..484f2178d 100644 --- a/src/renderer/views/wallet/send/SendViewDOGE.tsx +++ b/src/renderer/views/wallet/send/SendViewDOGE.tsx @@ -1,13 +1,11 @@ import React, { useMemo } from 'react' import * as RD from '@devexperts/remote-data-ts' -import { Address } from '@xchainjs/xchain-client' import { DOGEChain } from '@xchainjs/xchain-util' import * as FP from 'fp-ts/lib/function' import * as O from 'fp-ts/Option' import { useObservableState } from 'observable-hooks' -import { WalletType } from '../../../../shared/wallet/types' import { LoadingView } from '../../../components/shared/loading' import { SendFormDOGE } from '../../../components/wallet/txs/send/' import { useChainContext } from '../../../contexts/ChainContext' @@ -20,15 +18,14 @@ import { useValidateAddress } from '../../../hooks/useValidateAddress' import { WalletBalances } from '../../../services/clients' import { FeesWithRatesLD } from '../../../services/doge/types' import { DEFAULT_BALANCES_FILTER, INITIAL_BALANCES_STATE } from '../../../services/wallet/const' +import { SelectedWalletAsset } from '../../../services/wallet/types' type Props = { - walletType: WalletType - walletIndex: number - walletAddress: Address + asset: SelectedWalletAsset } export const SendViewDOGE: React.FC = (props): JSX.Element => { - const { walletType, walletIndex, walletAddress } = props + const { asset } = props const { network } = useNetwork() const { @@ -47,9 +44,9 @@ export const SendViewDOGE: React.FC = (props): JSX.Element => { () => FP.pipe( oBalances, - O.chain((balances) => getWalletBalanceByAddress(balances, walletAddress)) + O.chain((balances) => getWalletBalanceByAddress(balances, asset.walletAddress)) ), - [oBalances, walletAddress] + [asset.walletAddress, oBalances] ) const { transfer$ } = useChainContext() @@ -66,9 +63,7 @@ export const SendViewDOGE: React.FC = (props): JSX.Element => { () => , (walletBalance) => ( (() => []) diff --git a/src/renderer/views/wallet/send/SendViewETH.tsx b/src/renderer/views/wallet/send/SendViewETH.tsx index 49b379691..593485721 100644 --- a/src/renderer/views/wallet/send/SendViewETH.tsx +++ b/src/renderer/views/wallet/send/SendViewETH.tsx @@ -1,14 +1,12 @@ import React, { useMemo } from 'react' import * as RD from '@devexperts/remote-data-ts' -import { Address } from '@xchainjs/xchain-client' import { ETHAddress } from '@xchainjs/xchain-ethereum' -import { Asset, baseAmount, ETHChain } from '@xchainjs/xchain-util' +import { baseAmount, ETHChain } from '@xchainjs/xchain-util' import * as FP from 'fp-ts/lib/function' import * as O from 'fp-ts/lib/Option' import { useObservableState } from 'observable-hooks' -import { WalletType } from '../../../../shared/wallet/types' import { LoadingView } from '../../../components/shared/loading' import { SendFormETH } from '../../../components/wallet/txs/send/' import { useChainContext } from '../../../contexts/ChainContext' @@ -19,16 +17,14 @@ import { useNetwork } from '../../../hooks/useNetwork' import { useOpenExplorerTxUrl } from '../../../hooks/useOpenExplorerTxUrl' import { FeesRD, WalletBalances } from '../../../services/clients' import { DEFAULT_BALANCES_FILTER, INITIAL_BALANCES_STATE } from '../../../services/wallet/const' +import { SelectedWalletAsset } from '../../../services/wallet/types' type Props = { - walletType: WalletType - walletIndex: number - walletAddress: Address - asset: Asset + asset: SelectedWalletAsset } export const SendViewETH: React.FC = (props): JSX.Element => { - const { walletType, walletIndex, walletAddress, asset } = props + const { asset } = props const { network } = useNetwork() @@ -48,9 +44,11 @@ export const SendViewETH: React.FC = (props): JSX.Element => { () => FP.pipe( oBalances, - O.chain((balances) => getWalletBalanceByAddressAndAsset({ balances, address: walletAddress, asset })) + O.chain((balances) => + getWalletBalanceByAddressAndAsset({ balances, address: asset.walletAddress, asset: asset.asset }) + ) ), - [asset, oBalances, walletAddress] + [asset, oBalances] ) const { transfer$ } = useChainContext() @@ -63,7 +61,7 @@ export const SendViewETH: React.FC = (props): JSX.Element => { // `reloadFees` will be called and with it, `feesRD` will be updated with fees () => { return fees$({ - asset, + asset: asset.asset, amount: baseAmount(1), recipient: ETHAddress }) @@ -77,9 +75,7 @@ export const SendViewETH: React.FC = (props): JSX.Element => { () => , (walletBalance) => ( = (props): JSX.Element => { - const { walletType, walletIndex, walletAddress } = props + const { asset } = props const { network } = useNetwork() @@ -48,7 +45,7 @@ export const SendViewLTC: React.FC = (props): JSX.Element => { () => FP.pipe( oBalances, - O.chain((balances) => getWalletBalanceByAddress(balances, walletAddress)) + O.chain((balances) => getWalletBalanceByAddress(balances, asset.walletAddress)) ), [oBalances, walletAddress] ) @@ -68,9 +65,7 @@ export const SendViewLTC: React.FC = (props): JSX.Element => { () => , (walletBalance) => ( (() => []) diff --git a/src/renderer/views/wallet/send/SendViewTHOR.tsx b/src/renderer/views/wallet/send/SendViewTHOR.tsx index be4762cb3..760a95253 100644 --- a/src/renderer/views/wallet/send/SendViewTHOR.tsx +++ b/src/renderer/views/wallet/send/SendViewTHOR.tsx @@ -1,13 +1,11 @@ import React, { useMemo } from 'react' import * as RD from '@devexperts/remote-data-ts' -import { Address } from '@xchainjs/xchain-client' import { THORChain } from '@xchainjs/xchain-util' import * as FP from 'fp-ts/lib/function' import * as O from 'fp-ts/Option' import { useObservableState } from 'observable-hooks' -import { WalletType } from '../../../../shared/wallet/types' import { LoadingView } from '../../../components/shared/loading' import { SendFormTHOR } from '../../../components/wallet/txs/send/' import { useChainContext } from '../../../contexts/ChainContext' @@ -21,15 +19,14 @@ import { useValidateAddress } from '../../../hooks/useValidateAddress' import { FeeRD } from '../../../services/chain/types' import { WalletBalances } from '../../../services/clients' import { DEFAULT_BALANCES_FILTER, INITIAL_BALANCES_STATE } from '../../../services/wallet/const' +import { SelectedWalletAsset } from '../../../services/wallet/types' type Props = { - walletType: WalletType - walletIndex: number - walletAddress: Address + asset: SelectedWalletAsset } export const SendViewTHOR: React.FC = (props): JSX.Element => { - const { walletType, walletIndex, walletAddress } = props + const { asset } = props const { network } = useNetwork() const { @@ -48,9 +45,9 @@ export const SendViewTHOR: React.FC = (props): JSX.Element => { () => FP.pipe( oBalances, - O.chain((balances) => getWalletBalanceByAddress(balances, walletAddress)) + O.chain((balances) => getWalletBalanceByAddress(balances, asset.walletAddress)) ), - [oBalances, walletAddress] + [asset.walletAddress, oBalances] ) const { transfer$ } = useChainContext() @@ -74,8 +71,7 @@ export const SendViewTHOR: React.FC = (props): JSX.Element => { () => , (walletBalance) => ( (() => []) diff --git a/src/renderer/views/wallet/upgrade/UpgradeView.tsx b/src/renderer/views/wallet/upgrade/UpgradeView.tsx index 80b77c717..acae443a9 100644 --- a/src/renderer/views/wallet/upgrade/UpgradeView.tsx +++ b/src/renderer/views/wallet/upgrade/UpgradeView.tsx @@ -7,7 +7,6 @@ import * as FP from 'fp-ts/lib/function' import * as O from 'fp-ts/Option' import { useObservableState } from 'observable-hooks' import { useIntl } from 'react-intl' -import { useParams } from 'react-router-dom' import * as Rx from 'rxjs' import * as RxOp from 'rxjs/operators' @@ -19,18 +18,12 @@ import { useAppContext } from '../../../contexts/AppContext' import { useChainContext } from '../../../contexts/ChainContext' import { useMidgardContext } from '../../../contexts/MidgardContext' import { useWalletContext } from '../../../contexts/WalletContext' -import { getAssetFromNullableString, isNonNativeRuneAsset } from '../../../helpers/assetHelper' +import { isNonNativeRuneAsset } from '../../../helpers/assetHelper' import { eqOAsset } from '../../../helpers/fp/eq' import { sequenceSOption, sequenceTRD } from '../../../helpers/fpHelpers' -import { - addressFromOptionalWalletAddress, - getWalletAddressFromNullableString, - getWalletIndexFromNullableString, - getWalletTypeFromNullableString -} from '../../../helpers/walletHelper' +import { addressFromOptionalWalletAddress } from '../../../helpers/walletHelper' import { useOpenExplorerTxUrl } from '../../../hooks/useOpenExplorerTxUrl' import { useValidateAddress } from '../../../hooks/useValidateAddress' -import { AssetDetailsParams } from '../../../routes/wallet' import { AssetWithDecimalLD, AssetWithDecimalRD } from '../../../services/chain/types' import { DEFAULT_NETWORK } from '../../../services/const' import { PoolAddressRD } from '../../../services/midgard/types' @@ -44,18 +37,6 @@ import { UpgradeETH } from './UpgradeViewETH' type Props = {} export const UpgradeView: React.FC = (): JSX.Element => { - const { - asset: routeAsset, - walletAddress: routeWalletAddress, - walletIndex: routeWalletIndex, - walletType: routeWalletType - } = useParams() - - const oAsset = useMemo(() => getAssetFromNullableString(routeAsset), [routeAsset]) - const oWalletAddress = useMemo(() => getWalletAddressFromNullableString(routeWalletAddress), [routeWalletAddress]) - const oWalletIndex = useMemo(() => getWalletIndexFromNullableString(routeWalletIndex), [routeWalletIndex]) - const oWalletType = useMemo(() => getWalletTypeFromNullableString(routeWalletType), [routeWalletType]) - const intl = useIntl() const { network$ } = useAppContext() @@ -216,7 +197,7 @@ export const UpgradeView: React.FC = (): JSX.Element => { ) ), RD.fold( - () => <>, + () => , () => , renderDataError, ([runeAsset, { walletAddress, walletType, walletIndex }]) => diff --git a/src/renderer/views/wallet/upgrade/types.ts b/src/renderer/views/wallet/upgrade/types.ts index 583a39f05..f5ea2f93a 100644 --- a/src/renderer/views/wallet/upgrade/types.ts +++ b/src/renderer/views/wallet/upgrade/types.ts @@ -3,7 +3,7 @@ import * as FP from 'fp-ts/lib/function' import * as O from 'fp-ts/Option' import { Network } from '../../../../shared/api/types' -import { WalletType } from '../../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../../shared/wallet/types' import { UpgradeRuneParams, UpgradeRuneTxState$ } from '../../../services/chain/types' import { AddressValidation, GetExplorerTxUrl, OpenExplorerTxUrl } from '../../../services/clients' import { PoolAddressRD } from '../../../services/midgard/types' @@ -15,6 +15,7 @@ export type CommonUpgradeProps = { walletAddress: Address walletType: WalletType walletIndex: number + hdMode: HDMode runeNativeAddress: Address runeNativeLedgerAddress: O.Option
targetPoolAddressRD: PoolAddressRD diff --git a/src/shared/api/io.test.ts b/src/shared/api/io.test.ts index af3a1b4f9..2539d3960 100644 --- a/src/shared/api/io.test.ts +++ b/src/shared/api/io.test.ts @@ -96,7 +96,8 @@ describe('shared/io', () => { feeRate: 1, feeOption: FeeOption.Fast, feeAmount: baseAmount(1, 6), - nodeUrl: 'node-url' + nodeUrl: 'node-url', + hdMode: 'default' }) expect(encoded).toEqual({ chain: 'BNB', @@ -111,7 +112,8 @@ describe('shared/io', () => { feeRate: 1, feeOption: 'fast', feeAmount: { amount: '1', decimal: 6 }, - nodeUrl: 'node-url' + nodeUrl: 'node-url', + hdMode: 'default' }) }) @@ -129,7 +131,8 @@ describe('shared/io', () => { feeRate: 1, feeOption: undefined, feeAmount: undefined, - nodeUrl: undefined + nodeUrl: undefined, + hdMode: 'default' }) expect(encoded).toEqual({ @@ -145,7 +148,8 @@ describe('shared/io', () => { feeRate: 1, feeOption: undefined, feeAmount: undefined, - nodeUrl: undefined + nodeUrl: undefined, + hdMode: 'default' }) }) @@ -160,7 +164,8 @@ describe('shared/io', () => { memo: 'memo-abc', walletIndex: 0, feeRate: 1, - feeAmount: { amount: '1', decimal: 6 } + feeAmount: { amount: '1', decimal: 6 }, + hdMode: 'default' } const decoded = ipcLedgerSendTxParamsIO.decode(encoded) expect(E.isRight(decoded)).toBeTruthy() @@ -194,7 +199,8 @@ describe('shared/io', () => { memo: 'memo-abc', walletIndex: 0, feeRate: 1, - feeAmount: undefined + feeAmount: undefined, + hdMode: 'default' } const decoded = ipcLedgerSendTxParamsIO.decode(encoded) expect(E.isRight(decoded)).toBeTruthy() @@ -308,7 +314,7 @@ describe('ipcKeystorLedgerAddressesIO', () => { network: 'mainnet', address: 'eth-address', walletIndex: 1, - ethDerivationMode: 'metamask' + hdMode: 'metamask' }, { keystoreId: 1, @@ -316,7 +322,7 @@ describe('ipcKeystorLedgerAddressesIO', () => { network: 'stagenet', address: 'nbn-address', walletIndex: 2, - ethDerivationMode: undefined + hdMode: 'default' } ] diff --git a/src/shared/api/io.ts b/src/shared/api/io.ts index 0cb868de2..3fa695355 100644 --- a/src/shared/api/io.ts +++ b/src/shared/api/io.ts @@ -6,7 +6,7 @@ import * as t from 'io-ts' import * as IOD from 'io-ts/Decoder' import * as IOG from 'io-ts/Guard' -import { isAsset, isBaseAmount, isChain, isEthDerivationMode, isFeeOption, isNetwork } from '../utils/guard' +import { isAsset, isBaseAmount, isChain, isEthHDMode, isFeeOption, isHDMode, isNetwork } from '../utils/guard' const assetDecoder: IOD.Decoder = FP.pipe( IOD.string, @@ -76,12 +76,22 @@ export const networkIO = new t.Type( t.identity ) -export const ethDerivationModeIO = new t.Type( - 'EthDerivationMode', - isEthDerivationMode, +export const ethHDModeIO = new t.Type( + 'EthHDMode', + isEthHDMode, (u, c) => { - if (isEthDerivationMode(u)) return t.success(u) - return t.failure(u, c, `Can't decode EthDerivationMode from ${u}`) + if (isEthHDMode(u)) return t.success(u) + return t.failure(u, c, `Can't decode EthHDMode from ${u}`) + }, + t.identity +) + +export const hdModeIO = new t.Type( + 'HDMode', + isHDMode, + (u, c) => { + if (isHDMode(u)) return t.success(u) + return t.failure(u, c, `Can't decode HDMode from ${u}`) }, t.identity ) @@ -109,7 +119,8 @@ export const ipcLedgerSendTxParamsIO = t.type({ feeRate: t.number, feeOption: t.union([feeOptionIO, t.undefined]), feeAmount: t.union([baseAmountIO, t.undefined]), - nodeUrl: t.union([t.string, t.undefined]) + nodeUrl: t.union([t.string, t.undefined]), + hdMode: hdModeIO }) export type IPCLedgerSendTxParams = t.TypeOf @@ -124,7 +135,8 @@ export const ipcLedgerDepositTxParamsIO = t.type({ memo: t.string, walletIndex: t.number, feeOption: t.union([feeOptionIO, t.undefined]), - nodeUrl: t.union([t.string, t.undefined]) + nodeUrl: t.union([t.string, t.undefined]), + hdMode: hdModeIO }) export type IPCLedgerDepositTxParams = t.TypeOf @@ -133,7 +145,8 @@ export const ipcLedgerApproveERC20TokenParamsIO = t.type({ network: networkIO, contractAddress: t.string, spenderAddress: t.string, - walletIndex: t.number + walletIndex: t.number, + ethHdMode: ethHDModeIO }) export type IPCLedgerApproveERC20TokenParams = t.TypeOf @@ -227,7 +240,7 @@ export const ipcLedgerAddressIO = t.type({ chain: chainIO, network: networkIO, walletIndex: t.number, - ethDerivationMode: t.union([ethDerivationModeIO, t.undefined]) + hdMode: hdModeIO }) export const ipcLedgerAddressesIO = t.array(ipcLedgerAddressIO) diff --git a/src/shared/api/types.ts b/src/shared/api/types.ts index 1f9a932b2..a0e383735 100644 --- a/src/shared/api/types.ts +++ b/src/shared/api/types.ts @@ -6,15 +6,15 @@ import { Chain } from '@xchainjs/xchain-util' import * as E from 'fp-ts/lib/Either' import * as O from 'fp-ts/Option' -import { EthDerivationMode } from '../ethereum/types' +import { EthHDMode } from '../ethereum/types' import { Locale } from '../i18n/types' -import { WalletAddress } from '../wallet/types' +import { HDMode, WalletAddress } from '../wallet/types' import { IPCLedgerAddressesIO, KeystoreWallets, PoolsStorageEncoded } from './io' // A version number starting from `1` to avoid to load deprecated files export type StorageVersion = { version: string } export type UserNodesStorage = Readonly & StorageVersion> -export type CommonStorage = Readonly<{ locale: Locale; ethDerivationMode: EthDerivationMode } & StorageVersion> +export type CommonStorage = Readonly<{ locale: Locale; ethDerivationMode: EthHDMode } & StorageVersion> /** * Hash map of common store files @@ -124,7 +124,7 @@ export type IPCLedgerAdddressParams = { chain: Chain network: Network walletIndex: number - ethDerivationMode: EthDerivationMode | undefined + hdMode: HDMode } export type ApiHDWallet = { diff --git a/src/shared/ethereum/const.ts b/src/shared/ethereum/const.ts index fd3f7429d..ddecf9a3d 100644 --- a/src/shared/ethereum/const.ts +++ b/src/shared/ethereum/const.ts @@ -1,6 +1,6 @@ import { FeeBounds, Network } from '@xchainjs/xchain-client' -import { EthDerivationMode } from './types' +import { EthHDMode } from './types' export const DEFAULT_APPROVE_GAS_LIMIT_FALLBACK = '65000' @@ -14,4 +14,4 @@ export const FEE_BOUNDS: Record = { } } -export const DEFAULT_ETH_DERIVATION_MODE: EthDerivationMode = 'ledgerlive' +export const DEFAULT_ETH_DERIVATION_MODE: EthHDMode = 'ledgerlive' diff --git a/src/shared/ethereum/ledger.ts b/src/shared/ethereum/ledger.ts index 9d93247f2..39cf0b069 100644 --- a/src/shared/ethereum/ledger.ts +++ b/src/shared/ethereum/ledger.ts @@ -1,14 +1,14 @@ -import { EthDerivationMode } from './types' +import { EthHDMode } from './types' // ETH derivation pathes `Legacy`, `Ledger Live`, `MetaMask` // Based on // - Definitions in LedgerLive https://github.com/LedgerHQ/ledger-live/blob/develop/libs/ledger-live-common/src/derivation.ts#L43-L55 // - Definitions in MetaMask https://github.com/MetaMask/metamask-extension/blob/develop/ui/pages/create-account/connect-hardware/index.js#L24-L31 -const DERIVATION_MAP: Record = { +const DERIVATION_MAP: Record = { legacy: `m/44'/60'/0'/{account}`, ledgerlive: `m/44'/60'/{account}'/0/0`, metamask: `m/44'/60'/0'/0/{account}` } -export const getDerivationPath = (walletIndex: number, mode: EthDerivationMode): string => +export const getDerivationPath = (walletIndex: number, mode: EthHDMode): string => `${DERIVATION_MAP[mode]}`.replace(/{account}/, `${walletIndex}`) diff --git a/src/shared/ethereum/types.ts b/src/shared/ethereum/types.ts index e2307b8f2..96612c1b6 100644 --- a/src/shared/ethereum/types.ts +++ b/src/shared/ethereum/types.ts @@ -1 +1 @@ -export type EthDerivationMode = 'legacy' | 'ledgerlive' | 'metamask' +export type EthHDMode = 'legacy' | 'ledgerlive' | 'metamask' diff --git a/src/shared/mock/api.ts b/src/shared/mock/api.ts index a521d92f3..78c175379 100644 --- a/src/shared/mock/api.ts +++ b/src/shared/mock/api.ts @@ -27,7 +27,7 @@ export const apiUrl: ApiUrl = { // Mock `apiHDWallet` export const apiHDWallet: ApiHDWallet = { getLedgerAddress: ({ chain }) => - Promise.resolve(E.right({ chain, address: 'ledger_address', type: 'ledger', walletIndex: 0 })), + Promise.resolve(E.right({ chain, address: 'ledger_address', type: 'ledger', walletIndex: 0, hdMode: 'default' })), verifyLedgerAddress: () => Promise.resolve(true), sendLedgerTx: () => Promise.resolve(E.right('tx_hash')), depositLedgerTx: () => Promise.resolve(E.right('tx_hash')), diff --git a/src/shared/mock/wallet.ts b/src/shared/mock/wallet.ts index 90568ff98..5e283834b 100644 --- a/src/shared/mock/wallet.ts +++ b/src/shared/mock/wallet.ts @@ -27,37 +27,43 @@ export const MOCK_WALLET_ADDRESSES: WalletAddresses = [ address: 'tbnb1ed04qgw3s69z90jskr3shpyn9mr0e59qdtsxqa', type: 'ledger', chain: BNBChain, - walletIndex: 0 + walletIndex: 0, + hdMode: 'default' }, { address: 'tthor13gym97tmw3axj3hpewdggy2cr288d3qffr8skg', type: 'ledger', chain: THORChain, - walletIndex: 0 + walletIndex: 0, + hdMode: 'default' }, { address: '0x33292c1d02c432d323fb62c57fb327da45e1bdde', type: 'keystore', chain: ETHChain, - walletIndex: 0 + walletIndex: 0, + hdMode: 'default' }, { address: 'tb1qtephp596jhpwrawlp67junuk347zl2cwc56xml', type: 'keystore', chain: BTCChain, - walletIndex: 0 + walletIndex: 0, + hdMode: 'default' }, { address: 'qr20g55jd7x3dalp4qxjfgfvda0nwr8cfccrgxd0dw', type: 'keystore', chain: BCHChain, - walletIndex: 0 + walletIndex: 0, + hdMode: 'default' }, { address: 'tltc1qtephp596jhpwrawlp67junuk347zl2cwpucctk', type: 'keystore', chain: LTCChain, - walletIndex: 0 + walletIndex: 0, + hdMode: 'default' } ] diff --git a/src/shared/utils/guard.test.ts b/src/shared/utils/guard.test.ts index e1148370c..692ae2b24 100644 --- a/src/shared/utils/guard.test.ts +++ b/src/shared/utils/guard.test.ts @@ -6,7 +6,7 @@ import { isBaseAmount, isChain, isError, - isEthDerivationMode, + isEthHDMode, isFeeOption, isKeystoreWallet, isLedgerWallet, @@ -59,13 +59,13 @@ describe('shared/utils/guard', () => { describe('isNetwork', () => { it('legacy', () => { - expect(isEthDerivationMode('legacy')).toBeTruthy() + expect(isEthHDMode('legacy')).toBeTruthy() }) it('ledgerlive', () => { - expect(isEthDerivationMode('ledgerlive')).toBeTruthy() + expect(isEthHDMode('ledgerlive')).toBeTruthy() }) it('metamask', () => { - expect(isEthDerivationMode('metamask')).toBeTruthy() + expect(isEthHDMode('metamask')).toBeTruthy() }) it('false for invalid value', () => { expect(isNetwork('anything')).toBeFalsy() diff --git a/src/shared/utils/guard.ts b/src/shared/utils/guard.ts index 991484693..c911f4edc 100644 --- a/src/shared/utils/guard.ts +++ b/src/shared/utils/guard.ts @@ -6,8 +6,8 @@ import * as FP from 'fp-ts/lib/function' import * as IOG from 'io-ts/Guard' import { Network } from '../api/types' -import { EthDerivationMode } from '../ethereum/types' -import { WalletType } from '../wallet/types' +import { EthHDMode } from '../ethereum/types' +import { HDMode, WalletType } from '../wallet/types' export const nonEmptyStringGuard = FP.pipe( IOG.string, @@ -28,8 +28,10 @@ export const isFeeOption = (u: unknown): u is FeeOption => export const isWalletType = (u: unknown): u is WalletType => u === 'keystore' || u === 'ledger' export const isLedgerWallet = (walletType: WalletType): boolean => walletType === 'ledger' export const isKeystoreWallet = (walletType: WalletType): boolean => walletType === 'keystore' -export const isEthDerivationMode = (u: unknown): u is EthDerivationMode => - u === 'legacy' || u === 'ledgerlive' || u === 'metamask' + +export const isEthHDMode = (u: unknown): u is EthHDMode => u === 'legacy' || u === 'ledgerlive' || u === 'metamask' + +export const isHDMode = (u: unknown): u is HDMode => u === 'default' || isEthHDMode(u) const assetGuard = IOG.struct({ symbol: nonEmptyStringGuard, ticker: nonEmptyStringGuard, chain: chainGuard }) diff --git a/src/shared/wallet/types.ts b/src/shared/wallet/types.ts index ebb539d9f..804856484 100644 --- a/src/shared/wallet/types.ts +++ b/src/shared/wallet/types.ts @@ -1,9 +1,13 @@ import { Address } from '@xchainjs/xchain-client' import { Chain } from '@xchainjs/xchain-util' +import { EthHDMode } from '../ethereum/types' + export type WalletType = 'keystore' | 'ledger' export type WalletBalanceType = 'all' | 'confirmed' -export type WalletAddress = { address: Address; type: WalletType; chain: Chain; walletIndex: number } +export type HDMode = 'default' | EthHDMode + +export type WalletAddress = { address: Address; type: WalletType; chain: Chain; walletIndex: number; hdMode: HDMode } export type WalletAddresses = WalletAddress[] From a4652a929426a9bb9fbd1c14871d6ee47d9b217f Mon Sep 17 00:00:00 2001 From: Veado Date: Sun, 28 Aug 2022 18:14:16 +0200 Subject: [PATCH 2/2] Finish refactor --- .../components/deposit/add/SymDeposit.tsx | 2 + .../components/deposit/withdraw/Withdraw.tsx | 12 +- .../components/settings/WalletSettings.tsx | 22 +-- src/renderer/components/swap/Swap.tsx | 5 +- .../account/AccountSelector.stories.tsx | 3 +- .../wallet/assets/AssetDetails.stories.tsx | 7 - .../assets/AssetsTableCollapsable.stories.tsx | 11 +- .../wallet/assets/AssetsTableCollapsable.tsx | 92 ++---------- .../txs/interact/InteractForm.stories.tsx | 1 + .../wallet/txs/interact/InteractForm.tsx | 7 +- .../components/wallet/txs/send/Send.hooks.ts | 36 ----- .../wallet/txs/send/SendFormBCH.stories.tsx | 5 +- .../wallet/txs/send/SendFormBNB.stories.tsx | 5 +- .../wallet/txs/send/SendFormBNB.tsx | 2 +- .../wallet/txs/send/SendFormBTC.stories.tsx | 5 +- .../wallet/txs/send/SendFormCOSMOS.tsx | 2 +- .../wallet/txs/send/SendFormDOGE.stories.tsx | 5 +- .../wallet/txs/send/SendFormETH.stories.tsx | 13 +- .../wallet/txs/send/SendFormLTC.stories.tsx | 5 +- .../wallet/txs/send/SendFormTHOR.stories.tsx | 6 +- .../wallet/txs/upgrade/Upgrade.stories.tsx | 12 +- .../components/wallet/txs/upgrade/Upgrade.tsx | 85 +++++------ src/renderer/contexts/EthereumContext.tsx | 29 +--- src/renderer/helpers/fp/eq.ts | 4 - src/renderer/helpers/routeHelper.ts | 34 ----- src/renderer/hooks/useLedger.ts | 9 +- src/renderer/services/binance/balances.ts | 15 +- src/renderer/services/binance/types.ts | 3 +- src/renderer/services/bitcoin/types.ts | 3 +- src/renderer/services/bitcoincash/types.ts | 3 +- .../services/chain/transaction/common.ts | 33 ++++- .../services/chain/transaction/deposit.ts | 8 +- .../services/chain/transaction/swap.ts | 6 +- .../services/chain/transaction/upgrade.ts | 2 + src/renderer/services/chain/types.ts | 5 + src/renderer/services/cosmos/types.ts | 3 +- src/renderer/services/doge/types.ts | 3 +- src/renderer/services/ethereum/balances.ts | 2 +- src/renderer/services/ethereum/types.ts | 2 +- src/renderer/services/litecoin/types.ts | 3 +- src/renderer/services/thorchain/interact.ts | 6 +- .../services/thorchain/transaction.ts | 13 +- src/renderer/services/thorchain/types.ts | 5 +- src/renderer/services/wallet/balances.ts | 11 +- src/renderer/services/wallet/types.ts | 10 +- src/renderer/storybook/argTypes.ts | 5 + .../views/wallet/AssetDetailsView.tsx | 100 +++++++------ .../views/wallet/Interact/InteractView.tsx | 111 ++++++--------- .../views/wallet/WalletSettingsView.tsx | 42 +++--- .../views/wallet/send/SendViewLTC.tsx | 2 +- .../views/wallet/upgrade/UpgradeView.tsx | 133 ++++++++++-------- .../views/wallet/upgrade/UpgradeViewETH.tsx | 5 +- src/renderer/views/wallet/upgrade/types.ts | 14 +- src/shared/api/types.ts | 3 +- src/shared/const.ts | 4 +- src/shared/mock/api.ts | 3 +- 56 files changed, 404 insertions(+), 573 deletions(-) delete mode 100644 src/renderer/components/wallet/txs/send/Send.hooks.ts delete mode 100644 src/renderer/helpers/routeHelper.ts diff --git a/src/renderer/components/deposit/add/SymDeposit.tsx b/src/renderer/components/deposit/add/SymDeposit.tsx index 70898adf4..5f51d83c0 100644 --- a/src/renderer/components/deposit/add/SymDeposit.tsx +++ b/src/renderer/components/deposit/add/SymDeposit.tsx @@ -479,9 +479,11 @@ export const SymDeposit: React.FC = (props) => { }, runeWalletType: runeWB.walletType, runeWalletIndex: runeWB.walletIndex, + runeHDMode: runeWB.hdMode, runeSender: runeAddress, assetWalletType: assetWB.walletType, assetWalletIndex: assetWB.walletIndex, + assetHDMode: assetWB.hdMode, assetSender: assetAddress } }) diff --git a/src/renderer/components/deposit/withdraw/Withdraw.tsx b/src/renderer/components/deposit/withdraw/Withdraw.tsx index a25f3b76d..3edbabf97 100644 --- a/src/renderer/components/deposit/withdraw/Withdraw.tsx +++ b/src/renderer/components/deposit/withdraw/Withdraw.tsx @@ -118,7 +118,12 @@ export const Withdraw: React.FC = ({ const { asset, decimal: assetDecimal } = assetWD - const { type: runeWalletType, address: runeAddress, walletIndex: runeWalletIndex } = runeWalletAddress + const { + type: runeWalletType, + address: runeAddress, + walletIndex: runeWalletIndex, + hdMode: runeHDMode + } = runeWalletAddress const { type: assetWalletType, address: assetAddress } = assetWalletAddress // Disable withdraw in case all or pool actions are disabled @@ -365,10 +370,11 @@ export const Withdraw: React.FC = ({ network, memo, walletType: runeWalletType, - walletIndex: runeWalletIndex + walletIndex: runeWalletIndex, + hdMode: runeHDMode }) ) - }, [subscribeWithdrawState, withdraw$, network, memo, runeWalletType, runeWalletIndex]) + }, [subscribeWithdrawState, withdraw$, network, memo, runeWalletType, runeWalletIndex, runeHDMode]) const uiFeesRD: UIFeesRD = useMemo( () => diff --git a/src/renderer/components/settings/WalletSettings.tsx b/src/renderer/components/settings/WalletSettings.tsx index 862675644..056599bd2 100644 --- a/src/renderer/components/settings/WalletSettings.tsx +++ b/src/renderer/components/settings/WalletSettings.tsx @@ -29,7 +29,7 @@ import { KeystoreId, Network } from '../../../shared/api/types' import { getDerivationPath as getEthDerivationPath } from '../../../shared/ethereum/ledger' import { EthHDMode } from '../../../shared/ethereum/types' import { isError } from '../../../shared/utils/guard' -import { WalletAddress } from '../../../shared/wallet/types' +import { HDMode, WalletAddress } from '../../../shared/wallet/types' import { ReactComponent as UnlockOutlined } from '../../assets/svg/icon-unlock-warning.svg' import { WalletPasswordConfirmationModal } from '../../components/modal/confirmation' import { RemoveWalletConfirmationModal } from '../../components/modal/confirmation/RemoveWalletConfirmationModal' @@ -78,16 +78,8 @@ type Props = { changeKeystoreWallet$: ChangeKeystoreWalletHandler renameKeystoreWallet$: RenameKeystoreWalletHandler exportKeystore: () => Promise - addLedgerAddress$: (params: { - chain: Chain - walletIndex: number - ethDerivationMode: O.Option - }) => LedgerAddressLD - verifyLedgerAddress$: (params: { - chain: Chain - walletIndex: number - ethDerivationMode: O.Option - }) => VerifiedLedgerAddressLD + addLedgerAddress$: (params: { chain: Chain; walletIndex: number; hdMode: HDMode }) => LedgerAddressLD + verifyLedgerAddress$: (params: { chain: Chain; walletIndex: number; hdMode: HDMode }) => VerifiedLedgerAddressLD removeLedgerAddress: (chain: Chain) => void keystoreUnlocked: KeystoreUnlocked wallets: KeystoreWalletsUI @@ -225,7 +217,7 @@ export const WalletSettings: React.FC = (props): JSX.Element => { addLedgerAddress$({ chain, walletIndex, - ethDerivationMode: isEthChain(chain) ? O.some(ethDerivationMode) : O.none + hdMode: isEthChain(chain) ? ethDerivationMode : 'default' }) ) }, @@ -234,17 +226,17 @@ export const WalletSettings: React.FC = (props): JSX.Element => { const verifyLedgerAddressHandler = useCallback( (walletAddress: WalletAddress) => { - const { chain, walletIndex, address } = walletAddress + const { chain, walletIndex, address, hdMode } = walletAddress setLedgerAddressToVerify(O.some({ chain, address })) subscribeVerifyLedgerAddressRD( verifyLedgerAddress$({ chain, walletIndex, - ethDerivationMode: isEthChain(chain) ? O.some(ethDerivationMode) : O.none + hdMode }) ) }, - [ethDerivationMode, subscribeVerifyLedgerAddressRD, verifyLedgerAddress$] + [subscribeVerifyLedgerAddressRD, verifyLedgerAddress$] ) const renderLedgerAddress = useCallback( diff --git a/src/renderer/components/swap/Swap.tsx b/src/renderer/components/swap/Swap.tsx index 9287f2a2d..ba2e47d73 100644 --- a/src/renderer/components/swap/Swap.tsx +++ b/src/renderer/components/swap/Swap.tsx @@ -485,7 +485,7 @@ export const Swap = ({ () => FP.pipe( sequenceTOption(assetsToSwap, oPoolAddress, oTargetAddress, oSourceAssetWB), - O.map(([{ source, target }, poolAddress, address, { walletType, walletAddress, walletIndex }]) => { + O.map(([{ source, target }, poolAddress, address, { walletType, walletAddress, walletIndex, hdMode }]) => { const memo = getSwapMemo({ asset: target, address, @@ -499,7 +499,8 @@ export const Swap = ({ memo, walletType, sender: walletAddress, - walletIndex + walletIndex, + hdMode } }) ), diff --git a/src/renderer/components/wallet/account/AccountSelector.stories.tsx b/src/renderer/components/wallet/account/AccountSelector.stories.tsx index 74261bb96..eb92548dd 100644 --- a/src/renderer/components/wallet/account/AccountSelector.stories.tsx +++ b/src/renderer/components/wallet/account/AccountSelector.stories.tsx @@ -16,7 +16,8 @@ const few: WalletBalances = [AssetBNB, ASSETS_MAINNET.TOMO].map(( asset, amount: assetToBase(assetAmount(1)), walletAddress: `${assetToString(asset)} wallet`, - walletIndex: 0 + walletIndex: 0, + hdMode: 'default' })) const meta: ComponentMeta = { diff --git a/src/renderer/components/wallet/assets/AssetDetails.stories.tsx b/src/renderer/components/wallet/assets/AssetDetails.stories.tsx index de3d3944d..08ee90b93 100644 --- a/src/renderer/components/wallet/assets/AssetDetails.stories.tsx +++ b/src/renderer/components/wallet/assets/AssetDetails.stories.tsx @@ -37,7 +37,6 @@ export const StoryBNB: BaseStory = () => ( = () => ( = () => ( = () => ( = () => ( = () => ( = () => ( > = { { walletType: 'keystore', walletAddress: O.some('bnb keystore'), - walletIndex: 0, + chain: BNBChain, - hdMode: 'default', balances: RD.success([ { walletType: 'keystore', @@ -67,9 +66,7 @@ const balances: Partial> = { { walletType: 'keystore', walletAddress: O.some('btc keystore'), - walletIndex: 0, chain: BTCChain, - hdMode: 'default', balances: RD.success([ { walletType: 'keystore', @@ -87,8 +84,6 @@ const balances: Partial> = { { walletType: 'keystore', walletAddress: O.some('eth keystore'), - walletIndex: 0, - hdMode: 'default', chain: ETHChain, balances: RD.success([ { @@ -107,8 +102,6 @@ const balances: Partial> = { { walletType: 'keystore', walletAddress: O.some('thor keystore'), - walletIndex: 0, - hdMode: 'default', chain: THORChain, balances: RD.success([ { @@ -127,8 +120,6 @@ const balances: Partial> = { { walletType: 'keystore', walletAddress: O.some('ltc keystore'), - walletIndex: 0, - hdMode: 'default', chain: LTCChain, balances: RD.success([ { diff --git a/src/renderer/components/wallet/assets/AssetsTableCollapsable.tsx b/src/renderer/components/wallet/assets/AssetsTableCollapsable.tsx index 0c5f43dca..960586c76 100644 --- a/src/renderer/components/wallet/assets/AssetsTableCollapsable.tsx +++ b/src/renderer/components/wallet/assets/AssetsTableCollapsable.tsx @@ -13,7 +13,6 @@ import { useIntl } from 'react-intl' import { Network } from '../../../../shared/api/types' import { isKeystoreWallet } from '../../../../shared/utils/guard' -import { HDMode, WalletType } from '../../../../shared/wallet/types' import { disableRuneUpgrade, isNonNativeRuneAsset, isUSDAsset } from '../../../helpers/assetHelper' import { getChainAsset } from '../../../helpers/chainHelper' import { getPoolPriceValue } from '../../../helpers/poolHelper' @@ -70,7 +69,7 @@ export const AssetsTableCollapsable: React.FC = (props): JSX.Element => { const [collapseChangedByUser, setCollapseChangedByUser] = useState(false) // store previous data of asset data to render these while reloading - const previousAssetsTableData = useRef([]) + const previousAssetsTableData = useRef([]) // get halt status from Mimir const { haltThorChain, haltEthChain, haltBnbChain } = useMemo( @@ -83,30 +82,10 @@ export const AssetsTableCollapsable: React.FC = (props): JSX.Element => { ) const onRowHandler = useCallback( - ({ - oWalletAddress, - walletType, - walletIndex, - hdMode - }: { - oWalletAddress: O.Option
- walletType: WalletType - walletIndex: number - hdMode: HDMode - }) => - ({ asset }: Balance) => { - // Disable click for NativeRUNE if Thorchain is halted - const onClick = FP.pipe( - oWalletAddress, - O.map((walletAddress) => () => selectAssetHandler({ asset, walletAddress, walletType, walletIndex, hdMode })), - // TODO(@Veado) Add error message / alert - O.getOrElse(() => () => console.error('Unknown address')) - ) - - return { - onClick - } - }, + ({ asset, walletAddress, walletType, walletIndex, hdMode }: WalletBalance) => ({ + // Disable click for NativeRUNE if Thorchain is halted + onClick: () => selectAssetHandler({ asset, walletAddress, walletType, walletIndex, hdMode }) + }), [selectAssetHandler] ) @@ -255,66 +234,34 @@ export const AssetsTableCollapsable: React.FC = (props): JSX.Element => { }, [balanceColumn, iconColumn, priceColumn, screenMap, tickerColumn]) const renderAssetsTable = useCallback( - ({ - tableData, - oWalletAddress, - loading = false, - walletType, - walletIndex, - hdMode - }: { - tableData: Balance[] - oWalletAddress: O.Option
- loading?: boolean - walletType: WalletType - walletIndex: number - hdMode: HDMode - }) => { + ({ tableData, loading = false }: { tableData: WalletBalances; loading?: boolean }) => { return ( asset.symbol} - onRow={onRowHandler({ oWalletAddress, walletType, walletIndex, hdMode })} + onRow={onRowHandler} columns={columns} /> ) }, - [onRowHandler, columns] + [columns, onRowHandler] ) const renderBalances = useCallback( - ({ - balancesRD, - index, - oWalletAddress, - walletType, - walletIndex, - hdMode - }: { - balancesRD: WalletBalancesRD - index: number - oWalletAddress: O.Option
- walletType: WalletType - walletIndex: number - hdMode: HDMode - }) => { + ({ balancesRD, index }: { balancesRD: WalletBalancesRD; index: number }) => { return FP.pipe( balancesRD, RD.fold( // initial state - () => renderAssetsTable({ tableData: [], oWalletAddress, loading: false, walletType, walletIndex, hdMode }), + () => renderAssetsTable({ tableData: [], loading: false }), // loading state () => { const data = previousAssetsTableData.current[index] ?? [] return renderAssetsTable({ tableData: data, - oWalletAddress, - loading: true, - walletType, - walletIndex, - hdMode + loading: true }) }, // error state @@ -327,11 +274,7 @@ export const AssetsTableCollapsable: React.FC = (props): JSX.Element => { prev[index] = balances return renderAssetsTable({ tableData: balances, - oWalletAddress, - loading: false, - walletType, - walletIndex, - hdMode + loading: false }) } ) @@ -342,10 +285,7 @@ export const AssetsTableCollapsable: React.FC = (props): JSX.Element => { // Panel const renderPanel = useCallback( - ( - { chain, walletType, walletAddress: oWalletAddress, balances: balancesRD, walletIndex, hdMode }: ChainBalance, - key: number - ) => { + ({ chain, walletType, walletAddress: oWalletAddress, balances: balancesRD }: ChainBalance, key: number) => { /** * We need to push initial value to the ledger-based streams * 'cuz chainBalances$ stream is created by 'combineLatest' @@ -418,11 +358,7 @@ export const AssetsTableCollapsable: React.FC = (props): JSX.Element => { {renderBalances({ balancesRD, - index: key, - oWalletAddress, - walletType, - walletIndex, - hdMode + index: key })} ) diff --git a/src/renderer/components/wallet/txs/interact/InteractForm.stories.tsx b/src/renderer/components/wallet/txs/interact/InteractForm.stories.tsx index e9e07c419..f0574548b 100644 --- a/src/renderer/components/wallet/txs/interact/InteractForm.stories.tsx +++ b/src/renderer/components/wallet/txs/interact/InteractForm.stories.tsx @@ -71,6 +71,7 @@ const Template = ({ interactType, txRDStatus, feeRDStatus, balance, validAddress interactType={interactType} walletType={walletType} walletIndex={0} + hdMode="default" interact$={interact$} balance={runeBalance} addressValidation={(_: string) => validAddress} diff --git a/src/renderer/components/wallet/txs/interact/InteractForm.tsx b/src/renderer/components/wallet/txs/interact/InteractForm.tsx index c4ecc1530..631652b75 100644 --- a/src/renderer/components/wallet/txs/interact/InteractForm.tsx +++ b/src/renderer/components/wallet/txs/interact/InteractForm.tsx @@ -21,7 +21,7 @@ import { useIntl } from 'react-intl' import { Network } from '../../../../../shared/api/types' import { isKeystoreWallet, isLedgerWallet } from '../../../../../shared/utils/guard' -import { WalletType } from '../../../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../../../shared/wallet/types' import { ZERO_BASE_AMOUNT } from '../../../../const' import { THORCHAIN_DECIMAL } from '../../../../helpers/assetHelper' import { validateAddress } from '../../../../helpers/form/validation' @@ -50,6 +50,7 @@ type Props = { interactType: InteractType walletType: WalletType walletIndex: number + hdMode: HDMode balance: WalletBalance interact$: InteractStateHandler openExplorerTxUrl: OpenExplorerTxUrl @@ -65,6 +66,7 @@ export const InteractForm: React.FC = (props) => { interactType, balance, walletType, + hdMode, walletIndex, interact$, openExplorerTxUrl, @@ -253,11 +255,12 @@ export const InteractForm: React.FC = (props) => { interact$({ walletType, walletIndex, + hdMode, amount: amountToSend, memo: getMemo() }) ) - }, [subscribeInteractState, interact$, walletType, walletIndex, amountToSend, getMemo]) + }, [subscribeInteractState, interact$, walletType, walletIndex, hdMode, amountToSend, getMemo]) const [showConfirmationModal, setShowConfirmationModal] = useState(false) diff --git a/src/renderer/components/wallet/txs/send/Send.hooks.ts b/src/renderer/components/wallet/txs/send/Send.hooks.ts deleted file mode 100644 index 080279155..000000000 --- a/src/renderer/components/wallet/txs/send/Send.hooks.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { useCallback } from 'react' - -import { Address } from '@xchainjs/xchain-client' -import { Asset, assetToString } from '@xchainjs/xchain-util' -import { useNavigate } from 'react-router-dom' - -import { WalletType } from '../../../../../shared/wallet/types' -import * as walletRoutes from '../../../../routes/wallet' - -export const useChangeAssetHandler = () => { - const navigate = useNavigate() - - const handler = useCallback( - ({ - asset, - walletAddress, - walletType, - walletIndex - }: { - asset: Asset - walletAddress: Address - walletType: WalletType - walletIndex: number - }) => { - const path = walletRoutes.send.path({ - asset: assetToString(asset), - walletAddress, - walletType, - walletIndex: walletIndex.toString() - }) - navigate(path) - }, - [navigate] - ) - return handler -} diff --git a/src/renderer/components/wallet/txs/send/SendFormBCH.stories.tsx b/src/renderer/components/wallet/txs/send/SendFormBCH.stories.tsx index ddc4c1232..53e9db105 100644 --- a/src/renderer/components/wallet/txs/send/SendFormBCH.stories.tsx +++ b/src/renderer/components/wallet/txs/send/SendFormBCH.stories.tsx @@ -72,10 +72,7 @@ const Template = ({ txRDStatus, feeRDStatus, balance, validAddress, walletType } return ( { +const Template = ({ txRDStatus, feeRDStatus, balance, walletType }: Args) => { const transfer$: SendTxStateHandler = (_) => Rx.of({ steps: { current: txRDStatus === 'initial' ? 0 : 1, total: 1 }, @@ -64,10 +63,7 @@ const Template = ({ txRDStatus, feeRDStatus, balance, walletType, hdMode }: Args return ( = { walletType: { control: { type: 'select', options: ['keystore', 'ledger'] } }, - hdMode: { - control: { type: 'select', options: ['default', 'ledgerlive', 'metamask', 'legacy'] } - }, balance: { control: { type: 'text' } } diff --git a/src/renderer/components/wallet/txs/send/SendFormLTC.stories.tsx b/src/renderer/components/wallet/txs/send/SendFormLTC.stories.tsx index 14208f665..9968ff62b 100644 --- a/src/renderer/components/wallet/txs/send/SendFormLTC.stories.tsx +++ b/src/renderer/components/wallet/txs/send/SendFormLTC.stories.tsx @@ -72,10 +72,7 @@ const Template = ({ txRDStatus, feeRDStatus, balance, validAddress, walletType } return ( validAddress} - walletType={walletType} - walletIndex={0} - hdMode="default" - targetPoolAddressRD={RD.success({ chain: BNBChain, address: 'bnb-pool-address', router: O.none, halted: false })} + targetPoolAddress={{ chain: BNBChain, address: 'bnb-pool-address', router: O.none, halted: false }} validatePassword$={mockValidatePassword$} fee={feeRD} upgrade$={upgrade$} diff --git a/src/renderer/components/wallet/txs/upgrade/Upgrade.tsx b/src/renderer/components/wallet/txs/upgrade/Upgrade.tsx index 4ef40d6ae..d52ba7672 100644 --- a/src/renderer/components/wallet/txs/upgrade/Upgrade.tsx +++ b/src/renderer/components/wallet/txs/upgrade/Upgrade.tsx @@ -62,10 +62,9 @@ type FormValues = { export const Upgrade: React.FC = (props): JSX.Element => { const { - runeAsset, runeNativeAddress: initialRuneNativeAddress, runeNativeLedgerAddress: oRuneNativeLedgerAddress, - targetPoolAddressRD, + targetPoolAddress, validatePassword$, fee: feeRD, upgrade$, @@ -76,10 +75,7 @@ export const Upgrade: React.FC = (props): JSX.Element => { reloadFeeHandler, addressValidation, network, - walletAddress, - walletType, - walletIndex, - hdMode + assetData: { asset, walletAddress, walletType, walletIndex, hdMode, decimal: assetDecimal } } = props const intl = useIntl() @@ -136,13 +132,12 @@ export const Upgrade: React.FC = (props): JSX.Element => { const getRuneBalance = useMemo( () => getWalletAssetAmountFromBalances( - (balance) => - eqString.equals(balance.walletAddress, walletAddress) && eqAsset.equals(balance.asset, runeAsset.asset) + (balance) => eqString.equals(balance.walletAddress, walletAddress) && eqAsset.equals(balance.asset, asset) ), - [runeAsset, walletAddress] + [asset, walletAddress] ) - const chainBaseAsset = useMemo(() => getChainAsset(runeAsset.asset.chain), [runeAsset]) + const chainBaseAsset = useMemo(() => getChainAsset(asset.chain), [asset]) const getBaseAssetBalance = useMemo( () => @@ -199,11 +194,11 @@ export const Upgrade: React.FC = (props): JSX.Element => { // we have to validate input before storing into the state amountValidator(undefined, value) .then(() => { - setAmountToUpgrade(convertBaseAmountDecimal(assetToBase(assetAmount(value)), runeAsset.decimal)) + setAmountToUpgrade(convertBaseAmountDecimal(assetToBase(assetAmount(value)), assetDecimal)) }) .catch(() => {}) // do nothing, Ant' form does the job for us to show an error message }, - [amountValidator, runeAsset] + [amountValidator, assetDecimal] ) // Send tx start time @@ -212,36 +207,31 @@ export const Upgrade: React.FC = (props): JSX.Element => { const submitTx = useCallback(() => { setSendTxStartTime(Date.now()) - return FP.pipe( - targetPoolAddressRD, - RD.toOption, - O.map((poolAddress) => { - subscribeUpgradeTxState( - upgrade$({ - poolAddress, - amount: amountToUpgrade, - asset: runeAsset.asset, - memo: getSwitchMemo(form.getFieldValue('address')), - network, - walletAddress, - walletIndex, - walletType - }) - ) - return true + subscribeUpgradeTxState( + upgrade$({ + poolAddress: targetPoolAddress, + amount: amountToUpgrade, + asset, + memo: getSwitchMemo(form.getFieldValue('address')), + network, + walletAddress, + walletIndex, + walletType, + hdMode }) ) }, [ - targetPoolAddressRD, subscribeUpgradeTxState, upgrade$, + targetPoolAddress, amountToUpgrade, - runeAsset.asset, + asset, form, network, walletAddress, walletIndex, - walletType + walletType, + hdMode ]) const oFee: O.Option = useMemo(() => FP.pipe(feeRD, RD.toOption), [feeRD]) @@ -302,7 +292,7 @@ export const Upgrade: React.FC = (props): JSX.Element => { const txStatusMsg = useMemo(() => { const stepDescriptions = [ intl.formatMessage({ id: 'common.tx.healthCheck' }), - intl.formatMessage({ id: 'common.tx.sendingAsset' }, { assetTicker: runeAsset.asset.ticker }), + intl.formatMessage({ id: 'common.tx.sendingAsset' }, { assetTicker: asset.ticker }), intl.formatMessage({ id: 'common.tx.checkResult' }) ] const { steps, status } = upgradeTxState @@ -320,7 +310,7 @@ export const Upgrade: React.FC = (props): JSX.Element => { () => emptyString ) ) - }, [intl, runeAsset.asset.ticker, upgradeTxState]) + }, [intl, asset.ticker, upgradeTxState]) const addMaxAmountHandler = useCallback(() => setAmountToUpgrade(maxAmount), [maxAmount]) @@ -338,17 +328,8 @@ export const Upgrade: React.FC = (props): JSX.Element => { ) const reloadFees = useCallback(() => { - FP.pipe( - targetPoolAddressRD, - RD.toOption, - O.map((poolAddress) => { - reloadFeeHandler({ asset: runeAsset.asset, amount: amountToUpgrade, recipient: poolAddress.address }) - return true - }) - ) - - return false - }, [targetPoolAddressRD, reloadFeeHandler, runeAsset.asset, amountToUpgrade]) + reloadFeeHandler({ asset, amount: amountToUpgrade, recipient: targetPoolAddress.address }) + }, [targetPoolAddress, reloadFeeHandler, asset, amountToUpgrade]) const addressValidator = useCallback( async (_: unknown, value: string) => { @@ -418,7 +399,7 @@ export const Upgrade: React.FC = (props): JSX.Element => { ) } - const chain = runeAsset.asset.chain + const { chain } = asset const chainAsString = chainToString(chain) const txtNeedsConnected = intl.formatMessage( { @@ -455,7 +436,7 @@ export const Upgrade: React.FC = (props): JSX.Element => { ) } return null - }, [intl, network, runeAsset.asset.chain, showConfirmationModal, submitTx, validatePassword$, walletType]) + }, [asset, intl, network, showConfirmationModal, submitTx, validatePassword$, walletType]) const renderTxModal = useMemo(() => { const { status } = upgradeTxState @@ -509,7 +490,7 @@ export const Upgrade: React.FC = (props): JSX.Element => { )} extra={ @@ -519,13 +500,13 @@ export const Upgrade: React.FC = (props): JSX.Element => { }, [ upgradeTxState, intl, - resetUpgradeTxState, sendTxStartTime, openExplorerTxUrl, getExplorerTxUrl, - runeAsset.asset, + asset, amountToUpgrade, network, + resetUpgradeTxState, reloadBalancesHandler ]) @@ -537,7 +518,7 @@ export const Upgrade: React.FC = (props): JSX.Element => { selectedWallet={{ walletType, amount: assetToBase(assetAmount(0)), - asset: runeAsset.asset, + asset, walletAddress: '', walletIndex, hdMode @@ -564,7 +545,7 @@ export const Upgrade: React.FC = (props): JSX.Element => { diff --git a/src/renderer/contexts/EthereumContext.tsx b/src/renderer/contexts/EthereumContext.tsx index 971960a69..c1cf5ce3c 100644 --- a/src/renderer/contexts/EthereumContext.tsx +++ b/src/renderer/contexts/EthereumContext.tsx @@ -1,12 +1,5 @@ import React, { createContext, useContext } from 'react' -import * as FP from 'fp-ts/lib/function' -import * as O from 'fp-ts/lib/Option' -import * as Rx from 'rxjs' -import * as RxOp from 'rxjs/operators' - -import { DEFAULT_ETH_DERIVATION_MODE } from '../../shared/ethereum/const' -import { EthHDMode } from '../../shared/ethereum/types' import { client$, clientState$, @@ -26,7 +19,6 @@ import { approveFee$, reloadApproveFee } from '../services/ethereum' -import { getStorageState$, modifyStorage } from '../services/storage/common' export type EthereumContextValue = { client$: typeof client$ @@ -46,23 +38,6 @@ export type EthereumContextValue = { isApprovedERC20Token$: typeof isApprovedERC20Token$ approveFee$: typeof approveFee$ reloadApproveFee: typeof reloadApproveFee - ethDerivationMode$: Rx.Observable - updateEthDerivationMode: (m: EthHDMode) => void -} - -export const ethDerivationMode$ = FP.pipe( - getStorageState$, - RxOp.map( - FP.flow( - O.map(({ ethDerivationMode }) => ethDerivationMode), - O.getOrElse(() => DEFAULT_ETH_DERIVATION_MODE) - ) - ), - RxOp.distinctUntilChanged() -) - -export const updateEthDerivationMode = (mode: EthHDMode) => { - modifyStorage(O.some({ ethDerivationMode: mode })) } const initialContext: EthereumContextValue = { @@ -82,9 +57,7 @@ const initialContext: EthereumContextValue = { approveERC20Token$, isApprovedERC20Token$, approveFee$, - reloadApproveFee, - ethDerivationMode$, - updateEthDerivationMode + reloadApproveFee } const EthereumContext = createContext(null) diff --git a/src/renderer/helpers/fp/eq.ts b/src/renderer/helpers/fp/eq.ts index e84779faa..2028b1d61 100644 --- a/src/renderer/helpers/fp/eq.ts +++ b/src/renderer/helpers/fp/eq.ts @@ -11,7 +11,6 @@ import * as O from 'fp-ts/lib/Option' import * as S from 'fp-ts/lib/string' import { KeystoreId, LedgerError, Network } from '../../../shared/api/types' -import { EthHDMode } from '../../../shared/ethereum/types' import { HDMode, WalletAddress, WalletType } from '../../../shared/wallet/types' import { DepositAssetFees, DepositFees, SwapFeesParams, SymDepositAddresses } from '../../services/chain/types' import { ApproveParams } from '../../services/ethereum/types' @@ -108,9 +107,6 @@ export const eqAssetsWithBalanceRD = RD.getEq(eqApiError, e export const eqWalletType: Eq.Eq = eqString -export const eqEthDerivationMode: Eq.Eq = eqString -export const eqOEthDerivationMode = O.getEq(eqEthDerivationMode) - export const eqHDMode: Eq.Eq = eqString export const eqOHDMode = O.getEq(eqHDMode) diff --git a/src/renderer/helpers/routeHelper.ts b/src/renderer/helpers/routeHelper.ts deleted file mode 100644 index 5760ff99a..000000000 --- a/src/renderer/helpers/routeHelper.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Address } from '@xchainjs/xchain-client' - -import { WalletType } from '../../shared/wallet/types' -import { getAssetFromNullableString } from './assetHelper' -import { sequenceSOption } from './fpHelpers' -import { - getWalletAddressFromNullableString, - getWalletIndexFromNullableString, - getWalletTypeFromNullableString -} from './walletHelper' - -export const getAssetWalletParams = ({ - asset, - walletAddress, - walletIndex, - walletType -}: { - asset?: string - walletAddress?: Address - walletType?: WalletType - walletIndex?: string -}) => { - const oAsset = getAssetFromNullableString(asset) - const oWalletAddress = getWalletAddressFromNullableString(walletAddress) - const oWalletIndex = getWalletIndexFromNullableString(walletIndex) - const oWalletType = getWalletTypeFromNullableString(walletType) - - return sequenceSOption({ - asset: oAsset, - walletAddress: oWalletAddress, - walletIndex: oWalletIndex, - walletType: oWalletType - }) -} diff --git a/src/renderer/hooks/useLedger.ts b/src/renderer/hooks/useLedger.ts index 2ad19d2f8..1bfa567ee 100644 --- a/src/renderer/hooks/useLedger.ts +++ b/src/renderer/hooks/useLedger.ts @@ -7,8 +7,7 @@ import { useObservableState } from 'observable-hooks' import * as RxOp from 'rxjs/operators' import { KeystoreId } from '../../shared/api/types' -import { EthHDMode } from '../../shared/ethereum/types' -import { WalletAddress } from '../../shared/wallet/types' +import { HDMode, WalletAddress } from '../../shared/wallet/types' import { useWalletContext } from '../contexts/WalletContext' import { LedgerAddress } from '../services/wallet/types' import { ledgerAddressToWalletAddress } from '../services/wallet/util' @@ -20,8 +19,7 @@ export const useLedger = (chain: Chain, id: KeystoreId) => { const { addLedgerAddress$, getLedgerAddress$, verifyLedgerAddress$, removeLedgerAddress } = useWalletContext() const verifyAddress = useCallback( - (walletIndex: number, ethDerivationMode: O.Option) => - verifyLedgerAddress$({ chain, network, walletIndex, ethDerivationMode }), + (walletIndex: number, hdMode: HDMode) => verifyLedgerAddress$({ chain, network, walletIndex, hdMode }), [chain, verifyLedgerAddress$, network] ) const removeAddress = useCallback( @@ -40,8 +38,7 @@ export const useLedger = (chain: Chain, id: KeystoreId) => { ) const addAddress = useCallback( - (walletIndex: number, ethDerivationMode: O.Option) => - addLedgerAddress$({ id, chain, network, walletIndex, ethDerivationMode }), + (walletIndex: number, hdMode: HDMode) => addLedgerAddress$({ id, chain, network, walletIndex, hdMode }), [addLedgerAddress$, chain, id, network] ) diff --git a/src/renderer/services/binance/balances.ts b/src/renderer/services/binance/balances.ts index c055bb446..a08c4af81 100644 --- a/src/renderer/services/binance/balances.ts +++ b/src/renderer/services/binance/balances.ts @@ -2,7 +2,7 @@ import * as A from 'fp-ts/lib/Array' import * as FP from 'fp-ts/lib/function' import { Network } from '../../../shared/api/types' -import { WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../shared/wallet/types' import { assetInBinanceBlacklist } from '../../helpers/assetHelper' import { liveData } from '../../helpers/rx/liveData' import { observableState } from '../../helpers/stateHelper' @@ -25,13 +25,14 @@ const reloadBalances = () => { } // State of balances loaded by Client -const balances$: (walletType: WalletType, network: Network, walletIndex: number) => C.WalletBalancesLD = ( - walletType, - network, - walletIndex -) => +const balances$: ( + walletType: WalletType, + network: Network, + walletIndex: number, + hdMode: HDMode +) => C.WalletBalancesLD = (walletType, network, walletIndex, hdMode) => FP.pipe( - C.balances$({ client$, trigger$: reloadBalances$, walletType, walletIndex, walletBalanceType: 'all' }), + C.balances$({ client$, trigger$: reloadBalances$, walletType, walletIndex, hdMode, walletBalanceType: 'all' }), // Filter out black listed assets liveData.map(FP.flow(A.filter(({ asset }) => !assetInBinanceBlacklist(network, asset)))) ) diff --git a/src/renderer/services/binance/types.ts b/src/renderer/services/binance/types.ts index 7b203112b..f9a69eff1 100644 --- a/src/renderer/services/binance/types.ts +++ b/src/renderer/services/binance/types.ts @@ -6,7 +6,7 @@ import * as O from 'fp-ts/lib/Option' import * as Rx from 'rxjs' import { LedgerBNBTxParams, Network } from '../../../shared/api/types' -import { WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../shared/wallet/types' import { LiveData } from '../../helpers/rx/liveData' import * as C from '../clients' import { ApiError, LedgerTxHashLD } from '../wallet/types' @@ -55,6 +55,7 @@ export type SendTxParams = { asset: Asset memo?: string walletIndex: number + hdMode: HDMode } export type TransactionService = C.TransactionService diff --git a/src/renderer/services/bitcoin/types.ts b/src/renderer/services/bitcoin/types.ts index eea774b06..44f65d0e6 100644 --- a/src/renderer/services/bitcoin/types.ts +++ b/src/renderer/services/bitcoin/types.ts @@ -5,7 +5,7 @@ import { BaseAmount } from '@xchainjs/xchain-util' import * as Rx from 'rxjs' import { LedgerBTCTxInfo, Network } from '../../../shared/api/types' -import { WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../shared/wallet/types' import { LiveData } from '../../helpers/rx/liveData' import { Memo } from '../chain/types' import * as C from '../clients' @@ -30,6 +30,7 @@ export type SendTxParams = { feeRate: number memo?: string walletIndex: number + hdMode: HDMode } export type TransactionService = C.TransactionService diff --git a/src/renderer/services/bitcoincash/types.ts b/src/renderer/services/bitcoincash/types.ts index d65eeee8a..e658f0450 100644 --- a/src/renderer/services/bitcoincash/types.ts +++ b/src/renderer/services/bitcoincash/types.ts @@ -3,7 +3,7 @@ import { Client } from '@xchainjs/xchain-bitcoincash' import { Address, FeeRate, FeesWithRates } from '@xchainjs/xchain-client' import { BaseAmount } from '@xchainjs/xchain-util' -import { WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../shared/wallet/types' import { LiveData } from '../../helpers/rx/liveData' import { Memo } from '../chain/types' import * as C from '../clients' @@ -27,6 +27,7 @@ export type SendTxParams = { feeRate: number memo?: string walletIndex: number + hdMode: HDMode } export type TransactionService = C.TransactionService diff --git a/src/renderer/services/chain/transaction/common.ts b/src/renderer/services/chain/transaction/common.ts index 82154d7d1..1e0e517f0 100644 --- a/src/renderer/services/chain/transaction/common.ts +++ b/src/renderer/services/chain/transaction/common.ts @@ -52,7 +52,7 @@ export const sendTx$ = ({ }: SendTxParams): TxHashLD => { switch (asset.chain) { case BNBChain: - return BNB.sendTx({ walletType, sender, recipient, amount, asset, memo, walletIndex }) + return BNB.sendTx({ walletType, sender, recipient, amount, asset, memo, walletIndex, hdMode }) case BTCChain: return FP.pipe( @@ -70,7 +70,7 @@ export const sendTx$ = ({ return ETH.sendTx({ walletType, asset, recipient, amount, memo, feeOption, walletIndex, hdMode }) case THORChain: - return THOR.sendTx({ walletType, amount, asset, memo, recipient, walletIndex }) + return THOR.sendTx({ walletType, amount, asset, memo, recipient, walletIndex, hdMode }) case CosmosChain: return FP.pipe( @@ -82,7 +82,17 @@ export const sendTx$ = ({ liveData.chain((fees) => // fees for COSMOS are FLAT fees for now - different `feeOption` based still on same fee amount // If needed, we can change it later to have fee options (similar to Keplr wallet - search for `gasPriceStep` there) - COSMOS.sendTx({ walletType, sender, recipient, amount, asset, memo, walletIndex, feeAmount: fees[feeOption] }) + COSMOS.sendTx({ + walletType, + sender, + recipient, + amount, + asset, + memo, + walletIndex, + hdMode, + feeAmount: fees[feeOption] + }) ) ) @@ -102,7 +112,7 @@ export const sendTx$ = ({ msg: error?.message ?? error.toString() })), liveData.chain(({ rates }) => - DOGE.sendTx({ walletType, recipient, amount, feeRate: rates[feeOption], memo, walletIndex, sender }) + DOGE.sendTx({ walletType, recipient, amount, feeRate: rates[feeOption], memo, walletIndex, hdMode, sender }) ) ) @@ -114,7 +124,7 @@ export const sendTx$ = ({ msg: error?.message ?? error.toString() })), liveData.chain(({ rates }) => - BCH.sendTx({ walletType, recipient, amount, feeRate: rates[feeOption], memo, walletIndex, sender }) + BCH.sendTx({ walletType, recipient, amount, feeRate: rates[feeOption], memo, walletIndex, hdMode, sender }) ) ) case LTCChain: @@ -125,7 +135,16 @@ export const sendTx$ = ({ msg: error?.message ?? error.toString() })), liveData.chain(({ rates }) => { - return LTC.sendTx({ walletType, recipient, amount, feeRate: rates[feeOption], memo, walletIndex, sender }) + return LTC.sendTx({ + walletType, + recipient, + amount, + feeRate: rates[feeOption], + memo, + walletIndex, + hdMode, + sender + }) }) ) } @@ -161,7 +180,7 @@ export const sendPoolTx$ = ({ return THOR.sendPoolTx$({ walletType, amount, asset, memo, walletIndex, hdMode }) default: - return sendTx$({ sender, walletType, asset, recipient, amount, memo, feeOption, walletIndex }) + return sendTx$({ sender, walletType, asset, recipient, amount, memo, feeOption, walletIndex, hdMode }) } } diff --git a/src/renderer/services/chain/transaction/deposit.ts b/src/renderer/services/chain/transaction/deposit.ts index 53ea88bc1..fe87dfd30 100644 --- a/src/renderer/services/chain/transaction/deposit.ts +++ b/src/renderer/services/chain/transaction/deposit.ts @@ -44,7 +44,8 @@ export const asymDeposit$ = ({ amount, memo, walletType, - walletIndex + walletIndex, + hdMode }: AsymDepositParams): AsymDepositState$ => { // total of progress const total = O.some(100) @@ -80,6 +81,7 @@ export const asymDeposit$ = ({ return sendPoolTx$({ walletType, walletIndex, + hdMode, router: poolAddress.router, asset, recipient: poolAddress.address, @@ -171,9 +173,11 @@ export const symDeposit$ = ({ memos, runeWalletType, runeWalletIndex, + runeHDMode, runeSender, assetWalletType, assetWalletIndex, + assetHDMode, assetSender }: SymDepositParams): SymDepositState$ => { // total of progress @@ -208,6 +212,7 @@ export const symDeposit$ = ({ sender: assetSender, walletType: assetWalletType, walletIndex: assetWalletIndex, + hdMode: assetHDMode, router: poolAddresses.router, asset, recipient: poolAddresses.address, @@ -235,6 +240,7 @@ export const symDeposit$ = ({ sender: runeSender, walletType: runeWalletType, walletIndex: runeWalletIndex, + hdMode: runeHDMode, router: O.none, // no router for RUNE asset: AssetRuneNative, recipient: '', // no recipient for RUNE needed diff --git a/src/renderer/services/chain/transaction/swap.ts b/src/renderer/services/chain/transaction/swap.ts index cf68ea19e..b09592e72 100644 --- a/src/renderer/services/chain/transaction/swap.ts +++ b/src/renderer/services/chain/transaction/swap.ts @@ -33,7 +33,8 @@ export const swap$ = ({ memo, walletType, sender, - walletIndex + walletIndex, + hdMode }: SwapTxParams): SwapState$ => { // total of progress const total = O.some(100) @@ -74,7 +75,8 @@ export const swap$ = ({ memo, feeOption: ChainTxFeeOption.SWAP, sender, - walletIndex + walletIndex, + hdMode }) }), liveData.chain((txHash) => { diff --git a/src/renderer/services/chain/transaction/upgrade.ts b/src/renderer/services/chain/transaction/upgrade.ts index 51c125491..f2b280a45 100644 --- a/src/renderer/services/chain/transaction/upgrade.ts +++ b/src/renderer/services/chain/transaction/upgrade.ts @@ -23,6 +23,7 @@ export const upgradeRuneToNative$ = ({ walletAddress, walletType, walletIndex, + hdMode, asset, amount, memo, @@ -52,6 +53,7 @@ export const upgradeRuneToNative$ = ({ sender: walletAddress, walletType, walletIndex, + hdMode, router: poolAddresses.router, asset, recipient: poolAddresses.address, diff --git a/src/renderer/services/chain/types.ts b/src/renderer/services/chain/types.ts index 9e786e68f..7119dc159 100644 --- a/src/renderer/services/chain/types.ts +++ b/src/renderer/services/chain/types.ts @@ -68,6 +68,7 @@ export type AsymDepositParams = { readonly memo: string readonly walletIndex: number readonly walletType: WalletType + readonly hdMode: HDMode } export type SymDepositAmounts = { rune: BaseAmount; asset: BaseAmount } @@ -79,9 +80,11 @@ export type SymDepositParams = { readonly memos: SymDepositMemo readonly runeWalletType: WalletType readonly runeWalletIndex: number + readonly runeHDMode: HDMode readonly runeSender: Address readonly assetWalletIndex: number readonly assetWalletType: WalletType + readonly assetHDMode: HDMode readonly assetSender: Address } @@ -133,6 +136,7 @@ export type SwapTxParams = { readonly walletType: WalletType readonly sender: Address readonly walletIndex: number + readonly hdMode: HDMode } export type SwapStateHandler = (p: SwapTxParams) => SwapState$ @@ -274,6 +278,7 @@ export type UpgradeRuneParams = { readonly walletAddress: string readonly walletType: WalletType readonly walletIndex: number + readonly hdMode: HDMode readonly poolAddress: PoolAddress readonly asset: Asset readonly amount: BaseAmount diff --git a/src/renderer/services/cosmos/types.ts b/src/renderer/services/cosmos/types.ts index 9eb0453c4..275780e6f 100644 --- a/src/renderer/services/cosmos/types.ts +++ b/src/renderer/services/cosmos/types.ts @@ -2,7 +2,7 @@ import { Address } from '@xchainjs/xchain-client' import { Client } from '@xchainjs/xchain-cosmos' import { Asset, BaseAmount } from '@xchainjs/xchain-util' -import { WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../shared/wallet/types' import * as C from '../clients' export type Client$ = C.Client$ @@ -20,6 +20,7 @@ export type SendTxParams = { asset: Asset memo?: string walletIndex: number + hdMode: HDMode feeAmount: BaseAmount } diff --git a/src/renderer/services/doge/types.ts b/src/renderer/services/doge/types.ts index 0e82f7b3b..fc564917a 100644 --- a/src/renderer/services/doge/types.ts +++ b/src/renderer/services/doge/types.ts @@ -3,7 +3,7 @@ import { Address, FeeRate, FeesWithRates } from '@xchainjs/xchain-client' import { Client } from '@xchainjs/xchain-doge' import { BaseAmount } from '@xchainjs/xchain-util' -import { WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../shared/wallet/types' import { LiveData } from '../../helpers/rx/liveData' import { Memo } from '../chain/types' import * as C from '../clients' @@ -27,6 +27,7 @@ export type SendTxParams = { feeRate: number memo?: string walletIndex: number + hdMode: HDMode } export type TransactionService = C.TransactionService diff --git a/src/renderer/services/ethereum/balances.ts b/src/renderer/services/ethereum/balances.ts index 6516f25b7..7ff8eb2fe 100644 --- a/src/renderer/services/ethereum/balances.ts +++ b/src/renderer/services/ethereum/balances.ts @@ -37,7 +37,7 @@ const balances$: ({ network: Network walletIndex: number hdMode: HDMode -}) => C.WalletBalancesLD = ({ walletType, walletIndex, network }) => { +}) => C.WalletBalancesLD = ({ walletType, walletIndex, network, hdMode }) => { // For testnet we limit requests by using pre-defined assets only // because `xchain-ethereum` does for each asset a single request const assets: Asset[] | undefined = network === 'testnet' ? ETHAssetsTestnet : undefined diff --git a/src/renderer/services/ethereum/types.ts b/src/renderer/services/ethereum/types.ts index 7fc388aed..d25795154 100644 --- a/src/renderer/services/ethereum/types.ts +++ b/src/renderer/services/ethereum/types.ts @@ -33,8 +33,8 @@ export type SendTxParams = { memo: Memo feeOption: FeeOption walletIndex: number - walletType: WalletType hdMode: HDMode + walletType: WalletType } export type SendPoolTxParams = SendTxParams & { diff --git a/src/renderer/services/litecoin/types.ts b/src/renderer/services/litecoin/types.ts index 1c4bd6405..3539eabef 100644 --- a/src/renderer/services/litecoin/types.ts +++ b/src/renderer/services/litecoin/types.ts @@ -3,7 +3,7 @@ import { Address, FeesWithRates } from '@xchainjs/xchain-client' import { Client } from '@xchainjs/xchain-litecoin' import { BaseAmount } from '@xchainjs/xchain-util' -import { WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../shared/wallet/types' import { LiveData } from '../../helpers/rx/liveData' import { Memo } from '../chain/types' import * as C from '../clients' @@ -29,6 +29,7 @@ export type SendTxParams = { feeRate: number memo?: string walletIndex: number + hdMode: HDMode } export type TransactionService = C.TransactionService diff --git a/src/renderer/services/thorchain/interact.ts b/src/renderer/services/thorchain/interact.ts index da3f35150..9ce69efe2 100644 --- a/src/renderer/services/thorchain/interact.ts +++ b/src/renderer/services/thorchain/interact.ts @@ -7,7 +7,7 @@ import * as O from 'fp-ts/lib/Option' import * as Rx from 'rxjs' import * as RxOp from 'rxjs/operators' -import { WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../shared/wallet/types' import { liveData } from '../../helpers/rx/liveData' import { LiveData } from '../../helpers/rx/liveData' import { observableState } from '../../helpers/stateHelper' @@ -31,11 +31,12 @@ export const createInteractService$ = _: DepositParam & { walletType: WalletType walletIndex: number /* override walletIndex of DepositParam to avoid 'undefined' */ + hdMode: HDMode } ) => LiveData, getTxStatus: (txHash: string, assetAddress: O.Option
) => TxLD ) => - ({ walletType, walletIndex, amount, memo }: InteractParams): InteractState$ => { + ({ walletType, walletIndex, hdMode, amount, memo }: InteractParams): InteractState$ => { // total of progress const total = O.some(100) @@ -57,6 +58,7 @@ export const createInteractService$ = depositTx$({ walletType, walletIndex, + hdMode, asset: AssetRuneNative, amount, memo diff --git a/src/renderer/services/thorchain/transaction.ts b/src/renderer/services/thorchain/transaction.ts index 3aa849dc7..de068e55b 100644 --- a/src/renderer/services/thorchain/transaction.ts +++ b/src/renderer/services/thorchain/transaction.ts @@ -16,7 +16,7 @@ import { } from '../../../shared/api/io' import { LedgerError, Network } from '../../../shared/api/types' import { isLedgerWallet } from '../../../shared/utils/guard' -import { WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../shared/wallet/types' import { retryRequest } from '../../helpers/rx/retryRequest' import { Network$ } from '../app/types' import * as C from '../clients' @@ -38,7 +38,10 @@ export const createTransactionService = ( }: { network: Network clientUrl: ClientUrl - params: DepositParam & { walletIndex: number /* override walletIndex of DepositParam to avoid 'undefined' */ } + params: DepositParam & { + walletIndex: number /* override walletIndex of DepositParam to avoid 'undefined' */ + hdMode: HDMode + } }) => { const depositLedgerTxParams: IPCLedgerDepositTxParams = { chain: THORChain, @@ -51,7 +54,7 @@ export const createTransactionService = ( walletIndex: params.walletIndex, feeOption: undefined, nodeUrl: clientUrl[network].node, - hdMode: 'default' + hdMode: params.hdMode } const encoded = ipcLedgerDepositTxParamsIO.encode(depositLedgerTxParams) @@ -107,18 +110,20 @@ export const createTransactionService = ( const sendPoolTx = ({ walletType, walletIndex, + hdMode, asset, amount, memo }: DepositParam & { walletType: WalletType + hdMode: HDMode walletIndex: number /* override walletIndex of DepositParam to avoid 'undefined' */ }) => FP.pipe( Rx.combineLatest([network$, clientUrl$]), RxOp.switchMap(([network, clientUrl]) => { if (isLedgerWallet(walletType)) - return depositLedgerTx({ network, clientUrl, params: { walletIndex, asset, amount, memo } }) + return depositLedgerTx({ network, clientUrl, params: { walletIndex, hdMode, asset, amount, memo } }) return depositTx({ walletIndex, asset, amount, memo }) }) diff --git a/src/renderer/services/thorchain/types.ts b/src/renderer/services/thorchain/types.ts index 458084b98..f60e972f3 100644 --- a/src/renderer/services/thorchain/types.ts +++ b/src/renderer/services/thorchain/types.ts @@ -9,7 +9,7 @@ import { IntlShape } from 'react-intl' import * as Rx from 'rxjs' import { assetIO } from '../../../shared/api/io' -import { WalletType } from '../../../shared/wallet/types' +import { HDMode, WalletType } from '../../../shared/wallet/types' import { LiveData } from '../../helpers/rx/liveData' import { AssetsWithAmount1e8, AssetWithAmount1e8 } from '../../types/asgardex' import { NodeStatusEnum } from '../../types/generated/thornode' @@ -51,6 +51,7 @@ export type SendTxParams = { asset: Asset memo?: string walletIndex: number + hdMode: HDMode } export type TransactionService = { @@ -58,6 +59,7 @@ export type TransactionService = { params: DepositParam & { walletType: WalletType walletIndex: number /* override walletIndex of DepositParam to avoid 'undefined' */ + hdMode: HDMode } ) => TxHashLD } & C.TransactionService @@ -65,6 +67,7 @@ export type TransactionService = { export type InteractParams = { readonly walletType: WalletType readonly walletIndex: number + readonly hdMode: HDMode readonly amount: BaseAmount readonly memo: string } diff --git a/src/renderer/services/wallet/balances.ts b/src/renderer/services/wallet/balances.ts index d6c29e065..d9872e54e 100644 --- a/src/renderer/services/wallet/balances.ts +++ b/src/renderer/services/wallet/balances.ts @@ -122,7 +122,7 @@ export const createBalancesService = ({ resetReloadBalances: BNB.resetReloadBalances, balances$: FP.pipe( network$, - RxOp.switchMap((network) => BNB.balances$(walletType, network, walletIndex)) + RxOp.switchMap((network) => BNB.balances$(walletType, network, walletIndex, hdMode)) ), reloadBalances$: BNB.reloadBalances$ } @@ -320,7 +320,6 @@ export const createBalancesService = ({ chain, walletAddress: O.none, balances: RD.initial, - walletIndex: 0, balancesType: walletBalanceType }), ({ address, walletIndex, hdMode }) => @@ -330,11 +329,11 @@ export const createBalancesService = ({ getBalanceByAddress$({ address, walletType: 'ledger', walletIndex, walletBalanceType, hdMode }), RxOp.map((balances) => ({ walletType: 'ledger', - walletIndex, chain, walletAddress: O.some(address), balances, - balancesType: walletBalanceType + balancesType: walletBalanceType, + hdMode })) ) ) @@ -359,16 +358,16 @@ export const createBalancesService = ({ getChainBalance$({ chain: LTCChain, walletType: 'keystore', + // walletIndex=0 (as long as we don't support HD wallets for keystore) walletIndex: 0, hdMode: 'default', walletBalanceType: 'all' - }) // walletIndex=0 (as long as we don't support HD wallets for keystore) + }) ]).pipe( RxOp.map<[O.Option, WalletBalancesRD], ChainBalance>(([oWalletAddress, balances]) => ({ walletType: 'keystore', chain: LTCChain, walletAddress: addressFromOptionalWalletAddress(oWalletAddress), - walletIndex: 0, // Always 0 as long as we don't support HD wallets for keystore for keystore balances, balancesType: 'all' })) diff --git a/src/renderer/services/wallet/types.ts b/src/renderer/services/wallet/types.ts index 5a1b99a11..057798b01 100644 --- a/src/renderer/services/wallet/types.ts +++ b/src/renderer/services/wallet/types.ts @@ -113,14 +113,16 @@ export type SelectedWalletAsset = { walletIndex: number hdMode: HDMode } + +export type SelectedWalletAssetRD = RD.RemoteData +export type SelectedWalletAssetLD = LiveData /** - * Wraps WalletBalancesRD into an object to provide extra information (`Address` + `Chain` + `WalletType`) - * Currently needed in `AssetView` - TODO(@Veado) Think about to extract it into view layer (as helper or so) + * Wraps WalletBalancesRD into an object to provide extra information (`Address` + `Chain` + `WalletType` + `WalletBalanceType`) + * Currently needed in `AssetView` only + * TODO(@Veado) Think about to extract it into view layer (as helper or so) */ export type ChainBalance = { walletType: WalletType - walletIndex: number - hdMode: HDMode walletAddress: O.Option
chain: Chain balances: WalletBalancesRD diff --git a/src/renderer/storybook/argTypes.ts b/src/renderer/storybook/argTypes.ts index 375562bf8..1e1dad857 100644 --- a/src/renderer/storybook/argTypes.ts +++ b/src/renderer/storybook/argTypes.ts @@ -18,3 +18,8 @@ export const network = { options: ['mainnet', 'stagenet', 'testnet'] } } + +export const hdMode = { + name: 'HDMode', + control: { type: 'select', options: ['default', 'ledgerlive', 'metamask', 'legacy'] } +} diff --git a/src/renderer/views/wallet/AssetDetailsView.tsx b/src/renderer/views/wallet/AssetDetailsView.tsx index ed9d49f9d..1f8a2245e 100644 --- a/src/renderer/views/wallet/AssetDetailsView.tsx +++ b/src/renderer/views/wallet/AssetDetailsView.tsx @@ -7,18 +7,21 @@ import * as O from 'fp-ts/lib/Option' import * as NEA from 'fp-ts/NonEmptyArray' import { useObservableState } from 'observable-hooks' import * as Rx from 'rxjs' +import * as RxOp from 'rxjs/operators' import { LoadingView } from '../../components/shared/loading' -import { BackLink } from '../../components/uielements/backLink' import { AssetDetails } from '../../components/wallet/assets' import { useChainContext } from '../../contexts/ChainContext' import { useWalletContext } from '../../contexts/WalletContext' import { disableRuneUpgrade, isRuneNativeAsset } from '../../helpers/assetHelper' +import { eqOSelectedWalletAsset } from '../../helpers/fp/eq' import { sequenceTOption } from '../../helpers/fpHelpers' import { useMimirHalt } from '../../hooks/useMimirHalt' import { useNetwork } from '../../hooks/useNetwork' import { useOpenExplorerTxUrl } from '../../hooks/useOpenExplorerTxUrl' +import { TxsPageRD } from '../../services/clients' import { DEFAULT_BALANCES_FILTER, INITIAL_BALANCES_STATE } from '../../services/wallet/const' +import { SelectedWalletAsset } from '../../services/wallet/types' export const AssetDetailsView: React.FC = (): JSX.Element => { const { clientByChain$ } = useChainContext() @@ -31,13 +34,16 @@ export const AssetDetailsView: React.FC = (): JSX.Element => { const oSelectedAsset = useObservableState(selectedAsset$, O.none) - const [txsRD] = useObservableState( - () => + const [txsRD, updateTxsRD] = useObservableState>( + (selectedAssetUpdated$) => FP.pipe( - oSelectedAsset, - O.fold( - () => Rx.of(RD.pending), - ({ walletIndex, walletAddress }) => getTxs$(O.some(walletAddress), walletIndex) + selectedAssetUpdated$, + RxOp.distinctUntilChanged(eqOSelectedWalletAsset.equals), + RxOp.switchMap( + O.fold( + () => Rx.of(RD.pending), + ({ walletIndex, walletAddress }) => getTxs$(O.some(walletAddress), walletIndex) + ) ) ), RD.initial @@ -73,18 +79,27 @@ export const AssetDetailsView: React.FC = (): JSX.Element => { const { network } = useNetwork() - const [oClient] = useObservableState>( - () => + const [oClient, updateClient] = useObservableState, O.Option>( + (selectedAssetUpdated$) => FP.pipe( - oSelectedAsset, - O.fold( - () => Rx.of(O.none), - ({ asset }) => clientByChain$(asset.chain) + selectedAssetUpdated$, + RxOp.distinctUntilChanged(eqOSelectedWalletAsset.equals), + RxOp.switchMap( + O.fold( + () => Rx.of(O.none), + ({ asset }) => clientByChain$(asset.chain) + ) ) ), O.none ) + // Inform `useObservableState` about changes of `oSelectedAsset` + useEffect(() => { + updateTxsRD(oSelectedAsset) + updateClient(oSelectedAsset) + }, [oSelectedAsset, updateClient, updateTxsRD]) + const openExplorerAddressUrlHandler = useCallback(() => { FP.pipe( sequenceTOption(oClient, oSelectedAsset), @@ -103,37 +118,32 @@ export const AssetDetailsView: React.FC = (): JSX.Element => { ) ) - return ( - <> - - {FP.pipe( - oSelectedAsset, - O.fold( - () => , - ({ asset, walletAddress, walletType }) => ( - - ) - ) - )} - + return FP.pipe( + oSelectedAsset, + O.fold( + () => , + ({ asset, walletAddress, walletType }) => ( + + ) + ) ) } diff --git a/src/renderer/views/wallet/Interact/InteractView.tsx b/src/renderer/views/wallet/Interact/InteractView.tsx index d1ea58ca3..a03a8e43a 100644 --- a/src/renderer/views/wallet/Interact/InteractView.tsx +++ b/src/renderer/views/wallet/Interact/InteractView.tsx @@ -1,13 +1,13 @@ import React, { useCallback, useMemo } from 'react' import * as RD from '@devexperts/remote-data-ts' -import { AssetRuneNative, assetToString, THORChain } from '@xchainjs/xchain-util' +import { THORChain } from '@xchainjs/xchain-util' import { Col, Row } from 'antd' import * as FP from 'fp-ts/function' import * as O from 'fp-ts/Option' import { useObservableState } from 'observable-hooks' -import { useIntl } from 'react-intl' import { useNavigate, useParams } from 'react-router-dom' +import * as RxOp from 'rxjs/operators' import { ErrorView } from '../../../components/shared/error' import { LoadingView } from '../../../components/shared/loading' @@ -18,36 +18,39 @@ import { InteractType } from '../../../components/wallet/txs/interact/Interact.t import { InteractForm } from '../../../components/wallet/txs/interact/InteractForm' import { useThorchainContext } from '../../../contexts/ThorchainContext' import { useWalletContext } from '../../../contexts/WalletContext' -import { sequenceTOption } from '../../../helpers/fpHelpers' +import { eqOSelectedWalletAsset } from '../../../helpers/fp/eq' +import { sequenceTOption, sequenceTRD } from '../../../helpers/fpHelpers' import { liveData } from '../../../helpers/rx/liveData' -import { - getWalletAddressFromNullableString, - getWalletBalanceByAddress, - getWalletIndexFromNullableString, - getWalletTypeFromNullableString -} from '../../../helpers/walletHelper' +import { getWalletBalanceByAddress } from '../../../helpers/walletHelper' import { useNetwork } from '../../../hooks/useNetwork' import { useOpenExplorerTxUrl } from '../../../hooks/useOpenExplorerTxUrl' import { useValidateAddress } from '../../../hooks/useValidateAddress' import * as walletRoutes from '../../../routes/wallet' import { FeeRD } from '../../../services/chain/types' import { DEFAULT_BALANCES_FILTER, INITIAL_BALANCES_STATE } from '../../../services/wallet/const' +import { SelectedWalletAssetRD } from '../../../services/wallet/types' import * as Styled from './InteractView.styles' export const InteractView: React.FC = () => { - const { - interactType: routeInteractType, - walletAddress: routeWalletAddress, - walletType: routeWalletType, - walletIndex: routeWalletIndex - } = useParams() + const { interactType: routeInteractType } = useParams() + + const { selectedAsset$ } = useWalletContext() - const intl = useIntl() + const [selectedAssetRD] = useObservableState( + () => + FP.pipe( + selectedAsset$, + RxOp.distinctUntilChanged(eqOSelectedWalletAsset.equals), + RxOp.map((v) => v), + RxOp.map((oSelectedAsset) => RD.fromOption(oSelectedAsset, () => Error('No selected asset'))), + RxOp.startWith(RD.pending) + ), + RD.initial + ) - const oInteractType = getInteractTypeFromNullableString(routeInteractType) - const oWalletIndex = getWalletIndexFromNullableString(routeWalletIndex) - const oWalletType = getWalletTypeFromNullableString(routeWalletType) - const oWalletAddress = getWalletAddressFromNullableString(routeWalletAddress) + const interactTypeRD = FP.pipe(routeInteractType, getInteractTypeFromNullableString, (oInteractType) => + RD.fromOption(oInteractType, () => Error(`Invalid route param for interactive type: ${routeInteractType}`)) + ) const navigate = useNavigate() @@ -69,10 +72,12 @@ export const InteractView: React.FC = () => { const oWalletBalance = useMemo( () => FP.pipe( - sequenceTOption(oBalances, oWalletAddress), - O.chain(([balances, walletAddress]) => getWalletBalanceByAddress(balances, walletAddress)) + selectedAssetRD, + RD.toOption, + (oSelectedAsset) => sequenceTOption(oBalances, oSelectedAsset), + O.chain(([balances, { walletAddress }]) => getWalletBalanceByAddress(balances, walletAddress)) ), - [oBalances, oWalletAddress] + [oBalances, selectedAssetRD] ) const { interact$ } = useThorchainContext() @@ -90,56 +95,31 @@ export const InteractView: React.FC = () => { const interactTypeChanged = useCallback( (type: InteractType) => { - FP.pipe( - sequenceTOption(oWalletAddress, oWalletType, oWalletIndex), - O.map(([walletAddress, walletType, walletIndex]) => - navigate( - walletRoutes.interact.path({ - interactType: type, - walletAddress, - walletType, - walletIndex: walletIndex.toString() - }) - ) - ) + navigate( + walletRoutes.interact.path({ + interactType: type + }) ) }, - [navigate, oWalletAddress, oWalletType, oWalletIndex] - ) - - const renderRouteError = useMemo( - () => ( - <> - - - - ), - [intl, routeWalletAddress, routeWalletIndex, routeWalletType, routeInteractType] + [navigate] ) return FP.pipe( - sequenceTOption(oInteractType, oWalletAddress, oWalletType, oWalletIndex), - O.fold( - () => renderRouteError, - ([interactType, walletAddress, walletType, walletIndex]) => ( + sequenceTRD(interactTypeRD, selectedAssetRD), + RD.fold( + () => , + () => , + (error) => ( + <> + + + + ), + ([interactType, { walletType, walletIndex, hdMode }]) => ( <> - + @@ -157,6 +137,7 @@ export const InteractView: React.FC = () => { interactType={interactType} walletIndex={walletIndex} walletType={walletType} + hdMode={hdMode} balance={walletBalance} interact$={interact$} openExplorerTxUrl={openExplorerTxUrl} diff --git a/src/renderer/views/wallet/WalletSettingsView.tsx b/src/renderer/views/wallet/WalletSettingsView.tsx index db3a0f30e..c1d01b2f8 100644 --- a/src/renderer/views/wallet/WalletSettingsView.tsx +++ b/src/renderer/views/wallet/WalletSettingsView.tsx @@ -21,7 +21,7 @@ import * as Rx from 'rxjs' import * as RxOp from 'rxjs/operators' import { LedgerErrorId } from '../../../shared/api/types' -import { EthHDMode } from '../../../shared/ethereum/types' +import { HDMode } from '../../../shared/wallet/types' import { WalletSettings } from '../../components/settings' import { useBinanceContext } from '../../contexts/BinanceContext' import { useBitcoinCashContext } from '../../contexts/BitcoinCashContext' @@ -140,20 +140,20 @@ export const WalletSettingsView: React.FC = ({ keystoreUnlocked }): JSX.E const addLedgerAddressHandler = ({ chain, walletIndex, - ethDerivationMode + hdMode }: { chain: Chain walletIndex: number - ethDerivationMode: O.Option + hdMode: HDMode }): LedgerAddressLD => { - if (isThorChain(chain)) return addLedgerThorAddress(walletIndex, ethDerivationMode) - if (isBnbChain(chain)) return addLedgerBnbAddress(walletIndex, ethDerivationMode) - if (isBtcChain(chain)) return addLedgerBtcAddress(walletIndex, ethDerivationMode) - if (isLtcChain(chain)) return addLedgerLtcAddress(walletIndex, ethDerivationMode) - if (isBchChain(chain)) return addLedgerBchAddress(walletIndex, ethDerivationMode) - if (isDogeChain(chain)) return addLedgerDOGEAddress(walletIndex, ethDerivationMode) - if (isEthChain(chain)) return addLedgerEthAddress(walletIndex, ethDerivationMode) - if (isCosmosChain(chain)) return addLedgerCosmosAddress(walletIndex, ethDerivationMode) + if (isThorChain(chain)) return addLedgerThorAddress(walletIndex, hdMode) + if (isBnbChain(chain)) return addLedgerBnbAddress(walletIndex, hdMode) + if (isBtcChain(chain)) return addLedgerBtcAddress(walletIndex, hdMode) + if (isLtcChain(chain)) return addLedgerLtcAddress(walletIndex, hdMode) + if (isBchChain(chain)) return addLedgerBchAddress(walletIndex, hdMode) + if (isDogeChain(chain)) return addLedgerDOGEAddress(walletIndex, hdMode) + if (isEthChain(chain)) return addLedgerEthAddress(walletIndex, hdMode) + if (isCosmosChain(chain)) return addLedgerCosmosAddress(walletIndex, hdMode) return Rx.of( RD.failure({ @@ -166,20 +166,20 @@ export const WalletSettingsView: React.FC = ({ keystoreUnlocked }): JSX.E const verifyLedgerAddressHandler = ({ chain, walletIndex, - ethDerivationMode + hdMode }: { chain: Chain walletIndex: number - ethDerivationMode: O.Option + hdMode: HDMode }): VerifiedLedgerAddressLD => { - if (isThorChain(chain)) return verifyLedgerThorAddress(walletIndex, ethDerivationMode) - if (isBnbChain(chain)) return verifyLedgerBnbAddress(walletIndex, ethDerivationMode) - if (isBtcChain(chain)) return verifyLedgerBtcAddress(walletIndex, ethDerivationMode) - if (isLtcChain(chain)) return verifyLedgerLtcAddress(walletIndex, ethDerivationMode) - if (isBchChain(chain)) return verifyLedgerBchAddress(walletIndex, ethDerivationMode) - if (isDogeChain(chain)) return verifyLedgerDOGEAddress(walletIndex, ethDerivationMode) - if (isEthChain(chain)) return verifyLedgerEthAddress(walletIndex, ethDerivationMode) - if (isCosmosChain(chain)) return verifyLedgerCosmosAddress(walletIndex, ethDerivationMode) + if (isThorChain(chain)) return verifyLedgerThorAddress(walletIndex, hdMode) + if (isBnbChain(chain)) return verifyLedgerBnbAddress(walletIndex, hdMode) + if (isBtcChain(chain)) return verifyLedgerBtcAddress(walletIndex, hdMode) + if (isLtcChain(chain)) return verifyLedgerLtcAddress(walletIndex, hdMode) + if (isBchChain(chain)) return verifyLedgerBchAddress(walletIndex, hdMode) + if (isDogeChain(chain)) return verifyLedgerDOGEAddress(walletIndex, hdMode) + if (isEthChain(chain)) return verifyLedgerEthAddress(walletIndex, hdMode) + if (isCosmosChain(chain)) return verifyLedgerCosmosAddress(walletIndex, hdMode) return Rx.of(RD.failure(Error(`Ledger address verification for ${chain} has not been implemented`))) } diff --git a/src/renderer/views/wallet/send/SendViewLTC.tsx b/src/renderer/views/wallet/send/SendViewLTC.tsx index 9462583ac..893f29841 100644 --- a/src/renderer/views/wallet/send/SendViewLTC.tsx +++ b/src/renderer/views/wallet/send/SendViewLTC.tsx @@ -47,7 +47,7 @@ export const SendViewLTC: React.FC = (props): JSX.Element => { oBalances, O.chain((balances) => getWalletBalanceByAddress(balances, asset.walletAddress)) ), - [oBalances, walletAddress] + [asset.walletAddress, oBalances] ) const { transfer$ } = useChainContext() diff --git a/src/renderer/views/wallet/upgrade/UpgradeView.tsx b/src/renderer/views/wallet/upgrade/UpgradeView.tsx index acae443a9..c6610e429 100644 --- a/src/renderer/views/wallet/upgrade/UpgradeView.tsx +++ b/src/renderer/views/wallet/upgrade/UpgradeView.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useMemo } from 'react' import * as RD from '@devexperts/remote-data-ts' import { Address } from '@xchainjs/xchain-client' -import { Asset, BNBChain, ETHChain, THORChain } from '@xchainjs/xchain-util' +import { Asset, assetToString, BNBChain, Chain, ETHChain, THORChain } from '@xchainjs/xchain-util' import * as FP from 'fp-ts/lib/function' import * as O from 'fp-ts/Option' import { useObservableState } from 'observable-hooks' @@ -20,16 +20,17 @@ import { useMidgardContext } from '../../../contexts/MidgardContext' import { useWalletContext } from '../../../contexts/WalletContext' import { isNonNativeRuneAsset } from '../../../helpers/assetHelper' import { eqOAsset } from '../../../helpers/fp/eq' -import { sequenceSOption, sequenceTRD } from '../../../helpers/fpHelpers' +import { sequenceTRD } from '../../../helpers/fpHelpers' +import { liveData } from '../../../helpers/rx/liveData' import { addressFromOptionalWalletAddress } from '../../../helpers/walletHelper' import { useOpenExplorerTxUrl } from '../../../hooks/useOpenExplorerTxUrl' import { useValidateAddress } from '../../../hooks/useValidateAddress' -import { AssetWithDecimalLD, AssetWithDecimalRD } from '../../../services/chain/types' +import { AssetWithDecimalLD } from '../../../services/chain/types' import { DEFAULT_NETWORK } from '../../../services/const' import { PoolAddressRD } from '../../../services/midgard/types' import { DEFAULT_BALANCES_FILTER, INITIAL_BALANCES_STATE } from '../../../services/wallet/const' +import { SelectedWalletAssetRD } from '../../../services/wallet/types' import { ledgerAddressToWalletAddress } from '../../../services/wallet/util' -import { AssetWithDecimal } from '../../../types/asgardex' import { CommonUpgradeProps } from './types' import { UpgradeBNB } from './UpgradeViewBNB' import { UpgradeETH } from './UpgradeViewETH' @@ -46,25 +47,54 @@ export const UpgradeView: React.FC = (): JSX.Element => { const { addressByChain$, upgradeRuneToNative$, assetWithDecimal$ } = useChainContext() - // Accept [CHAIN].Rune only - const oRuneNonNativeAsset: O.Option = useMemo( - () => FP.pipe(oAsset, O.chain(O.fromPredicate((asset) => isNonNativeRuneAsset(asset, network)))), - [network, oAsset] + const { selectedAsset$ } = useWalletContext() + + const runeToUpgradeAssetRD$ = useMemo( + () => + FP.pipe( + Rx.combineLatest([selectedAsset$, network$]), + RxOp.map(([oSelectedAsset, network]) => + FP.pipe( + oSelectedAsset, + O.fold( + () => RD.failure(Error('No selected asset to upgrade')), + (selectedAsset) => + isNonNativeRuneAsset(selectedAsset.asset, network) + ? RD.success(selectedAsset) + : RD.failure(Error(`Invalid asset to upgrade ${assetToString(selectedAsset.asset)}`)) + ) + ) + ), + RxOp.startWith(RD.pending) + ), + [network$, selectedAsset$] ) - const [runeNonNativeAssetRD, updateRuneNonNativeAssetRD] = useObservableState>( - (oRuneNonNativeAsset$) => + const runeToUpgradeAssetRD = useObservableState(runeToUpgradeAssetRD$, RD.initial) + const oRuneToUpgradeAsset: O.Option = useMemo( + () => + FP.pipe( + RD.toOption(runeToUpgradeAssetRD), + O.map(({ asset }) => asset) + ), + [runeToUpgradeAssetRD] + ) + + const [runeToDecimalRD] = useObservableState>( + () => FP.pipe( // Note: network$ is needed to get latest network changes // using `network` from `useObservableState` above has no effect here - Rx.combineLatest([oRuneNonNativeAsset$, network$]), - RxOp.switchMap(([oAsset, network]) => + Rx.combineLatest([runeToUpgradeAssetRD$, network$]), + RxOp.switchMap(([assetRD, network]) => FP.pipe( - oAsset, - O.map((asset) => assetWithDecimal$(asset, network)), + RD.toOption(assetRD), + O.map(({ asset }) => assetWithDecimal$(asset, network)), O.getOrElse((): AssetWithDecimalLD => Rx.of(RD.initial)) ) - ) + ), + liveData.map(({ decimal }) => decimal), + RxOp.startWith(RD.pending) ), RD.initial ) @@ -117,11 +147,8 @@ export const UpgradeView: React.FC = (): JSX.Element => { RxOp.switchMap( FP.flow( O.fold( - // No subscription of `poolAddresses$ ` needed for other assets than [CHAIN].RUNE - () => - Rx.of( - RD.failure(Error(intl.formatMessage({ id: 'wallet.errors.asset.notExist' }, { asset: routeAsset }))) - ), + // No subscription of `poolAddresses$` needed for other assets than [CHAIN].RUNE + () => Rx.of(RD.initial), (asset) => poolAddressesByChain$(asset.chain) ) ) @@ -130,31 +157,25 @@ export const UpgradeView: React.FC = (): JSX.Element => { RD.initial ) - /** Effect to re-trigger calculations for useObservableState's */ + /** Effect to re-trigger calculations of useObservableState */ useEffect(() => { - updateTargetPoolAddressRD(oRuneNonNativeAsset) - updateRuneNonNativeAssetRD(oRuneNonNativeAsset) - }, [oRuneNonNativeAsset, updateRuneNonNativeAssetRD, updateTargetPoolAddressRD]) + updateTargetPoolAddressRD(oRuneToUpgradeAsset) + }, [oRuneToUpgradeAsset, updateTargetPoolAddressRD]) const renderDataError = useCallback( (error: Error) => { return ( ) }, - [intl, routeAsset, routeWalletAddress, routeWalletIndex, routeWalletType] + [intl] ) - const renderUpgradeComponent = useCallback(({ asset }: AssetWithDecimal, props: CommonUpgradeProps) => { - switch (asset.chain) { + const renderUpgradeComponent = useCallback((chain: Chain, props: CommonUpgradeProps) => { + switch (chain) { case BNBChain: { return } @@ -168,7 +189,7 @@ export const UpgradeView: React.FC = (): JSX.Element => { const { openExplorerTxUrl, getExplorerTxUrl } = useOpenExplorerTxUrl( FP.pipe( - oRuneNonNativeAsset, + oRuneToUpgradeAsset, O.map(({ chain }) => chain) ) ) @@ -177,57 +198,45 @@ export const UpgradeView: React.FC = (): JSX.Element => { <> {FP.pipe( - sequenceTRD( - runeNonNativeAssetRD, - RD.fromOption( - sequenceSOption({ - walletAddress: oWalletAddress, - walletIndex: oWalletIndex, - walletType: oWalletType - }), - () => - Error( - intl.formatMessage( - { id: 'routes.invalid.params' }, - { - params: `asset: ${routeAsset} , walletAddress: ${routeWalletAddress}, walletType: ${routeWalletType}, walletIndex: ${routeWalletIndex}` - } - ) - ) - ) - ), + sequenceTRD(runeToUpgradeAssetRD, runeToDecimalRD, targetPoolAddressRD), RD.fold( () => , () => , renderDataError, - ([runeAsset, { walletAddress, walletType, walletIndex }]) => - FP.pipe( + ([{ asset, walletType, walletAddress, walletIndex, hdMode }, decimal, targetPoolAddress]) => { + const runeToUpgradeChain = asset.chain + return FP.pipe( // Show an error by invalid address // All other values should be immediately available by entering the `UpgradeView` oRuneNativeAddress, O.fold( () => renderDataError(Error('Could not get address from asset')), (runeNativeAddress) => { - return renderUpgradeComponent(runeAsset, { + return renderUpgradeComponent(runeToUpgradeChain, { addressValidation: validateAddress, - walletAddress, - walletType, - walletIndex, - runeAsset, + assetData: { + asset, + walletAddress, + walletType, + walletIndex, + hdMode, + decimal + }, runeNativeAddress, runeNativeLedgerAddress: oRuneNativeLedgerAddress, - targetPoolAddressRD, + targetPoolAddress, validatePassword$, upgrade$: upgradeRuneToNative$, balances: oBalances, openExplorerTxUrl, getExplorerTxUrl, - reloadBalancesHandler: reloadBalancesByChain(runeAsset.asset.chain), + reloadBalancesHandler: reloadBalancesByChain(runeToUpgradeChain), network }) } ) ) + } ) )} diff --git a/src/renderer/views/wallet/upgrade/UpgradeViewETH.tsx b/src/renderer/views/wallet/upgrade/UpgradeViewETH.tsx index 25b2fc50f..5ea80fcd4 100644 --- a/src/renderer/views/wallet/upgrade/UpgradeViewETH.tsx +++ b/src/renderer/views/wallet/upgrade/UpgradeViewETH.tsx @@ -10,13 +10,16 @@ import { liveData } from '../../../helpers/rx/liveData' import { CommonUpgradeProps } from './types' export const UpgradeETH: React.FC = (props) => { + const { + assetData: { asset } + } = props const { fees$, reloadFees } = useEthereumContext() const [upgradeFeeRD] = useObservableState( () => FP.pipe( fees$({ - asset: props.runeAsset.asset, + asset, amount: baseAmount(1), recipient: ETHAddress }), diff --git a/src/renderer/views/wallet/upgrade/types.ts b/src/renderer/views/wallet/upgrade/types.ts index f5ea2f93a..7e39f041d 100644 --- a/src/renderer/views/wallet/upgrade/types.ts +++ b/src/renderer/views/wallet/upgrade/types.ts @@ -3,22 +3,16 @@ import * as FP from 'fp-ts/lib/function' import * as O from 'fp-ts/Option' import { Network } from '../../../../shared/api/types' -import { HDMode, WalletType } from '../../../../shared/wallet/types' import { UpgradeRuneParams, UpgradeRuneTxState$ } from '../../../services/chain/types' import { AddressValidation, GetExplorerTxUrl, OpenExplorerTxUrl } from '../../../services/clients' -import { PoolAddressRD } from '../../../services/midgard/types' -import { NonEmptyWalletBalances, ValidatePasswordHandler } from '../../../services/wallet/types' -import { AssetWithDecimal } from '../../../types/asgardex' +import { PoolAddress } from '../../../services/midgard/types' +import { NonEmptyWalletBalances, SelectedWalletAsset, ValidatePasswordHandler } from '../../../services/wallet/types' export type CommonUpgradeProps = { - runeAsset: AssetWithDecimal - walletAddress: Address - walletType: WalletType - walletIndex: number - hdMode: HDMode + assetData: SelectedWalletAsset & { decimal: number } runeNativeAddress: Address runeNativeLedgerAddress: O.Option
- targetPoolAddressRD: PoolAddressRD + targetPoolAddress: PoolAddress addressValidation: AddressValidation validatePassword$: ValidatePasswordHandler upgrade$: (_: UpgradeRuneParams) => UpgradeRuneTxState$ diff --git a/src/shared/api/types.ts b/src/shared/api/types.ts index a0e383735..413a9b04d 100644 --- a/src/shared/api/types.ts +++ b/src/shared/api/types.ts @@ -6,7 +6,6 @@ import { Chain } from '@xchainjs/xchain-util' import * as E from 'fp-ts/lib/Either' import * as O from 'fp-ts/Option' -import { EthHDMode } from '../ethereum/types' import { Locale } from '../i18n/types' import { HDMode, WalletAddress } from '../wallet/types' import { IPCLedgerAddressesIO, KeystoreWallets, PoolsStorageEncoded } from './io' @@ -14,7 +13,7 @@ import { IPCLedgerAddressesIO, KeystoreWallets, PoolsStorageEncoded } from './io // A version number starting from `1` to avoid to load deprecated files export type StorageVersion = { version: string } export type UserNodesStorage = Readonly & StorageVersion> -export type CommonStorage = Readonly<{ locale: Locale; ethDerivationMode: EthHDMode } & StorageVersion> +export type CommonStorage = Readonly<{ locale: Locale } & StorageVersion> /** * Hash map of common store files diff --git a/src/shared/const.ts b/src/shared/const.ts index 9e779ca8e..d8a2a6eea 100644 --- a/src/shared/const.ts +++ b/src/shared/const.ts @@ -33,13 +33,13 @@ const POOLS_STORAGE_DEFAULT: PoolsStorageEncoded = { } // increase it by `1` if you want to ignore previous version of `common` storage -const COMMON_STORAGE_VERSION = '1' +const COMMON_STORAGE_VERSION = '2' /** * When adding a new store file do not forget to expose * public api for it at src/main/preload.ts */ export const DEFAULT_STORAGES: StoreFilesContent = { - common: { version: COMMON_STORAGE_VERSION, locale: DEFAULT_LOCALE, ethDerivationMode: 'ledgerlive' }, + common: { version: COMMON_STORAGE_VERSION, locale: DEFAULT_LOCALE }, userNodes: USER_NODES_STORAGE_DEFAULT, pools: POOLS_STORAGE_DEFAULT } diff --git a/src/shared/mock/api.ts b/src/shared/mock/api.ts index 78c175379..2431be3d2 100644 --- a/src/shared/mock/api.ts +++ b/src/shared/mock/api.ts @@ -38,8 +38,7 @@ export const apiHDWallet: ApiHDWallet = { const commonStorageData: CommonStorage = { locale: Locale.EN, - ethDerivationMode: 'ledgerlive', - version: '1' + version: '2' } export const apiCommonStorage: ApiFileStoreService = {