Skip to content
This repository has been archived by the owner on Oct 10, 2023. It is now read-only.

Commit

Permalink
[Ledger] Add wallet index input for RUNE (#1889)
Browse files Browse the repository at this point in the history
* Enable ledger walletIndex for RUNE

* Add walletIndex to verify ledger address

* Code review suggestions
  • Loading branch information
gromxyz authored Oct 27, 2021
1 parent df4d4d3 commit 1403328
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 32 deletions.
4 changes: 2 additions & 2 deletions src/main/api/ledger/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const getAddress = async ({
const transport = await TransportNodeHidSingleton.open()
switch (chain) {
case THORChain:
res = await getTHORAddress(transport, network)
res = await getTHORAddress(transport, network, walletIndex)
break
case BNBChain:
res = await getBNBAddress(transport, network, walletIndex)
Expand All @@ -43,7 +43,7 @@ export const verifyLedgerAddress = async ({ chain, network, walletIndex = 0 }: I
const transport = await TransportNodeHidSingleton.open()
switch (chain) {
case THORChain:
verifyTHORAddress(transport, network)
verifyTHORAddress(transport, network, walletIndex)
break
case BNBChain:
verifyBNBAddress(transport, network, walletIndex)
Expand Down
13 changes: 8 additions & 5 deletions src/main/api/ledger/thorchain/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@ import { LedgerError, LedgerErrorId, Network } from '../../../../shared/api/type
import { toClientNetwork } from '../../../../shared/utils/client'
import { isError } from '../../../../shared/utils/guard'
import { WalletAddress } from '../../../../shared/wallet/types'
import { fromLedgerErrorType, PATH } from './common'
import { fromLedgerErrorType, getDerivationPath } from './common'

export const getAddress = async (
transport: Transport,
network: Network
network: Network,
walletIndex: number
): Promise<E.Either<LedgerError, WalletAddress>> => {
try {
const app = new THORChainApp(transport)
const clientNetwork = toClientNetwork(network)
const prefix = getPrefix(clientNetwork)
const { bech32Address, returnCode } = await app.getAddressAndPubKey(PATH, prefix)
const path = getDerivationPath(walletIndex)
const { bech32Address, returnCode } = await app.getAddressAndPubKey(path, prefix)
if (!bech32Address || returnCode !== LedgerErrorType.NoErrors) {
return E.left({
errorId: fromLedgerErrorType(returnCode),
Expand All @@ -34,9 +36,10 @@ export const getAddress = async (
}
}

export const verifyAddress = async (transport: Transport, network: Network) => {
export const verifyAddress = async (transport: Transport, network: Network, walletIndex: number) => {
const app = new THORChainApp(transport)
const clientNetwork = toClientNetwork(network)
const prefix = getPrefix(clientNetwork)
app.showAddressAndPubKey(PATH, prefix)
const path = getDerivationPath(walletIndex)
app.showAddressAndPubKey(path, prefix)
}
2 changes: 1 addition & 1 deletion src/main/api/ledger/thorchain/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { LedgerErrorType } from '@thorchain/ledger-thorchain'
import { LedgerErrorId } from '../../../../shared/api/types'

// TODO(@veado) Get path by using `xchain-thorchain`
export const PATH = [44, 931, 0, 0, 0]
export const getDerivationPath = (walletIndex = 0) => [44, 931, 0, 0, walletIndex]

export const fromLedgerErrorType = (error: number): LedgerErrorId => {
switch (error) {
Expand Down
20 changes: 13 additions & 7 deletions src/main/api/ledger/thorchain/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { LedgerError, LedgerErrorId, Network } from '../../../../shared/api/type
import { getClientUrl } from '../../../../shared/thorchain/client'
import { toClientNetwork } from '../../../../shared/utils/client'
import { isError } from '../../../../shared/utils/guard'
import { fromLedgerErrorType, PATH } from './common'
import { fromLedgerErrorType, getDerivationPath } from './common'

/**
* Sends `MsgSend` message using Ledger
Expand All @@ -33,21 +33,24 @@ export const send = async ({
network,
amount,
memo,
recipient
recipient,
walletIndex
}: {
transport: Transport
amount: BaseAmount
network: Network
recipient: Address
memo?: string
walletIndex: number
}): Promise<E.Either<LedgerError, TxHash>> => {
try {
const clientNetwork = toClientNetwork(network)
const prefix = getPrefix(clientNetwork)

const app = new THORChainApp(transport)
const path = getDerivationPath(walletIndex)
// get address + public key
const { bech32Address, returnCode, compressedPk } = await app.getAddressAndPubKey(PATH, prefix)
const { bech32Address, returnCode, compressedPk } = await app.getAddressAndPubKey(path, prefix)
if (!bech32Address || !compressedPk || returnCode !== LedgerErrorType.NoErrors) {
return E.left({
errorId: fromLedgerErrorType(returnCode),
Expand Down Expand Up @@ -107,7 +110,7 @@ export const send = async ({
const signedStdTx = unsignedStdTx.getSignBytes(chainId, account_number.toString(), sequence.toString())

// Sign StdTx
const { signature } = await app.sign(PATH, signedStdTx.toString())
const { signature } = await app.sign(path, signedStdTx.toString())

if (!signature) {
return E.left({
Expand Down Expand Up @@ -160,20 +163,23 @@ export const deposit = async ({
transport,
network,
amount,
memo
memo,
walletIndex
}: {
transport: Transport
amount: BaseAmount
network: Network
memo: string
walletIndex: number
}): Promise<E.Either<LedgerError, TxHash>> => {
try {
const clientNetwork = toClientNetwork(network)
const prefix = getPrefix(clientNetwork)

const app = new THORChainApp(transport)
const path = getDerivationPath(walletIndex)
// get address + public key
const { bech32Address, returnCode, compressedPk } = await app.getAddressAndPubKey(PATH, prefix)
const { bech32Address, returnCode, compressedPk } = await app.getAddressAndPubKey(path, prefix)
if (!bech32Address || !compressedPk || returnCode !== LedgerErrorType.NoErrors) {
return E.left({
errorId: fromLedgerErrorType(returnCode),
Expand Down Expand Up @@ -219,7 +225,7 @@ export const deposit = async ({
// Get bytes from StdTx to sign
const signedStdTx = unsignedStdTx.getSignBytes(chainId, account_number.toString(), sequence.toString())
// Sign StdTx
const { signature } = await app.sign(PATH, signedStdTx.toString())
const { signature } = await app.sign(path, signedStdTx.toString())

if (!signature) {
return E.left({
Expand Down
18 changes: 13 additions & 5 deletions src/main/api/ledger/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,21 @@ export const sendTx = async ({
amount,
asset,
memo,
walletIndex
walletIndex = 0
}: IPCLedgerSendTxParams): Promise<E.Either<LedgerError, TxHash>> => {
try {
const transport = await TransportNodeHidSingleton.open()
let res: E.Either<LedgerError, string>
switch (chain) {
case THORChain:
res = await THOR.send({ transport, network, recipient, amount, memo })
res = await THOR.send({
transport,
network,
recipient,
amount,
memo,
walletIndex
})
break
case BNBChain:
res = await BNB.send({
Expand All @@ -35,7 +42,7 @@ export const sendTx = async ({
amount,
asset,
memo,
walletIndex: walletIndex ? walletIndex : 0
walletIndex
})
break
default:
Expand All @@ -58,14 +65,15 @@ export const deposit = async ({
chain,
network,
amount,
memo
memo,
walletIndex
}: IPCLedgerDepositTxParams): Promise<E.Either<LedgerError, TxHash>> => {
try {
const transport = await TransportNodeHidSingleton.open()
let res: E.Either<LedgerError, string>
switch (chain) {
case THORChain:
res = await THOR.deposit({ transport, network, amount, memo })
res = await THOR.deposit({ transport, network, amount, memo, walletIndex: walletIndex ? walletIndex : 0 })
break
default:
res = E.left({
Expand Down
40 changes: 31 additions & 9 deletions src/renderer/components/wallet/settings/WalletSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,18 @@ import React, { useCallback, useMemo, useState } from 'react'

import * as RD from '@devexperts/remote-data-ts'
import { Address } from '@xchainjs/xchain-client'
import { Asset, Chain } from '@xchainjs/xchain-util'
import {
Asset,
BCHChain,
BNBChain,
BTCChain,
Chain,
CosmosChain,
ETHChain,
LTCChain,
PolkadotChain,
THORChain
} from '@xchainjs/xchain-util'
import { Col, List, Row } from 'antd'
import * as FP from 'fp-ts/function'
import * as O from 'fp-ts/lib/Option'
Expand All @@ -16,7 +27,7 @@ import { PasswordModal } from '../../../components/modal/password'
import { AssetIcon } from '../../../components/uielements/assets/assetIcon/AssetIcon'
import { QRCodeModal } from '../../../components/uielements/qrCodeModal/QRCodeModal'
import { PhraseCopyModal } from '../../../components/wallet/phrase/PhraseCopyModal'
import { getChainAsset, isBnbChain } from '../../../helpers/chainHelper'
import { getChainAsset, isBnbChain, isThorChain } from '../../../helpers/chainHelper'
import { ValidatePasswordHandler, WalletAccounts, WalletAddressAsync } from '../../../services/wallet/types'
import { walletTypeToI18n } from '../../../services/wallet/util'
import { InfoIcon } from '../../uielements/info'
Expand Down Expand Up @@ -72,7 +83,16 @@ export const WalletSettings: React.FC<Props> = (props): JSX.Element => {
const [showRemoveWalletModal, setShowRemoveWalletModal] = useState(false)
const [showQRModal, setShowQRModal] = useState<O.Option<{ asset: Asset; address: Address }>>(O.none)
const closeQrModal = useCallback(() => setShowQRModal(O.none), [setShowQRModal])
const [walletIndex, setWalletIndex] = useState<number>(0)
const [walletIndex, setWalletIndex] = useState<Record<Chain, number>>({
[BNBChain]: 0,
[BTCChain]: 0,
[BCHChain]: 0,
[LTCChain]: 0,
[THORChain]: 0,
[ETHChain]: 0,
[CosmosChain]: 0,
[PolkadotChain]: 0
})

const removeWallet = useCallback(() => {
removeKeystore()
Expand Down Expand Up @@ -105,18 +125,20 @@ export const WalletSettings: React.FC<Props> = (props): JSX.Element => {
(chain: Chain, { type: walletType, address: addressRD }: WalletAddressAsync) => {
const renderAddLedger = (chain: Chain, loading: boolean) => (
<Styled.AddLedgerContainer>
<Styled.AddLedgerButton loading={loading} onClick={() => addLedgerAddress(chain, walletIndex)}>
<Styled.AddLedgerButton loading={loading} onClick={() => addLedgerAddress(chain, walletIndex[chain])}>
<Styled.AddLedgerIcon /> {intl.formatMessage({ id: 'ledger.add.device' })}
</Styled.AddLedgerButton>
{isBnbChain(chain) && (
{(isBnbChain(chain) || isThorChain(chain)) && (
<>
<Styled.IndexLabel>{intl.formatMessage({ id: 'setting.wallet.index' })}</Styled.IndexLabel>
<Styled.WalletIndexInput
value={walletIndex.toString()}
value={walletIndex[chain].toString()}
pattern="[0-9]+"
onChange={(value) => value !== null && +value >= 0 && setWalletIndex(+value)}
onChange={(value) =>
value !== null && +value >= 0 && setWalletIndex({ ...walletIndex, [chain]: +value })
}
style={{ width: 60 }}
onPressEnter={() => addLedgerAddress(chain, walletIndex)}
onPressEnter={() => addLedgerAddress(chain, walletIndex[chain])}
/>
<InfoIcon tooltip={intl.formatMessage({ id: 'setting.wallet.index.info' })} />
</>
Expand Down Expand Up @@ -152,7 +174,7 @@ export const WalletSettings: React.FC<Props> = (props): JSX.Element => {
chain
})
)
verifyLedgerAddress(chain, walletIndex)
verifyLedgerAddress(chain, walletIndex[chain])
}}
/>
)}
Expand Down
3 changes: 2 additions & 1 deletion src/renderer/services/thorchain/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ export const createTransactionService = (client$: Client$, network$: Network$):
network,
asset: params.asset,
amount: params.amount,
memo: params.memo
memo: params.memo,
walletIndex: params.walletIndex
}
const encoded = ipcLedgerDepositTxParamsIO.encode(depositLedgerTxParams)

Expand Down
2 changes: 1 addition & 1 deletion src/renderer/views/wallet/WalletSettingsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export const WalletSettingsView: React.FC = (): JSX.Element => {
}

const verifyLedgerAddressHandler = (chain: Chain, walletIndex = 0) => {
if (isThorChain(chain)) return verifyLedgerThorAddress()
if (isThorChain(chain)) return verifyLedgerThorAddress(walletIndex)
if (isBnbChain(chain)) return verifyLedgerBnbAddress(walletIndex)

return FP.constVoid
Expand Down
3 changes: 2 additions & 1 deletion src/shared/api/io.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ export const ipcLedgerDepositTxParamsIO = t.type({
network: networkIO,
asset: t.union([assetIO, t.undefined]),
amount: baseAmountIO,
memo: t.string
memo: t.string,
walletIndex: t.union([t.number, t.undefined])
})

export type IPCLedgerDepositTxParams = t.TypeOf<typeof ipcLedgerDepositTxParamsIO>

0 comments on commit 1403328

Please sign in to comment.