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

Commit

Permalink
[Swap] Show address type for recipient (#1857)
Browse files Browse the repository at this point in the history
  • Loading branch information
gromxyz authored Oct 17, 2021
1 parent a567981 commit 97d8a95
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 18 deletions.
1 change: 1 addition & 0 deletions src/renderer/components/swap/EditableAddress.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const defaultProps: EditableAddressProps = {
network: 'testnet',
onClickOpenAddress: () => console.log('open address in explorer'),
onChangeAddress: () => console.log('address changed'),
onChangeEditableAddress: () => console.log('address changed'),
onChangeEditableMode: () => console.log('edit mode changed'),
addressValidator: (address: Address) => Promise.resolve(eqString.equals(address, bnbAddress))
}
Expand Down
19 changes: 13 additions & 6 deletions src/renderer/components/swap/EditableAddress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ export type EditableAddressProps = {
network: Network
onClickOpenAddress: (address: Address) => void
onChangeAddress: (address: Address) => void
onChangeEditableAddress: (address: Address) => void
onChangeEditableMode: (editModeActive: boolean) => void
addressValidator: AddressValidationAsync
}
export const EditableAddress = ({
asset,
address,
onChangeAddress,
onChangeEditableAddress,
onClickOpenAddress,
onChangeEditableMode,
addressValidator,
Expand Down Expand Up @@ -56,28 +58,33 @@ export const EditableAddress = ({
const confirmEditHandler = useCallback(() => {
if (form.getFieldError(RECIPIENT_FIELD).length === 0) {
onChangeAddress(form.getFieldValue(RECIPIENT_FIELD))
onChangeEditableAddress(form.getFieldValue(RECIPIENT_FIELD))
form.resetFields()
setEditableAddress(O.none)
onChangeEditableMode(false)
}
}, [form, onChangeAddress, onChangeEditableMode])
}, [form, onChangeAddress, onChangeEditableAddress, onChangeEditableMode])

const cancelEditHandler = useCallback(() => {
form.resetFields()
onChangeEditableAddress(address)
setEditableAddress(O.none)
onChangeEditableMode(false)
}, [form, onChangeEditableMode])
}, [address, form, onChangeEditableAddress, onChangeEditableMode])

const inputOnKeyDownHandler = useCallback(
const inputOnKeyUpHandler = useCallback(
(e) => {
// Call callback before handling key - in other case result will be lost
onChangeEditableAddress(form.getFieldValue(RECIPIENT_FIELD))

if (e.key === 'Enter') {
confirmEditHandler()
}
if (e.key === 'Escape') {
cancelEditHandler()
}
},
[cancelEditHandler, confirmEditHandler]
[cancelEditHandler, confirmEditHandler, form, onChangeEditableAddress]
)

const renderAddress = useMemo(() => {
Expand Down Expand Up @@ -110,7 +117,7 @@ export const EditableAddress = ({
recipient: editableAddress
}}>
<Form.Item rules={[{ required: true, validator: validateAddress }]} name={RECIPIENT_FIELD}>
<Styled.Input color="primary" onKeyDown={inputOnKeyDownHandler} />
<Styled.Input color="primary" onKeyUp={inputOnKeyUpHandler} />
</Form.Item>
</Styled.InnerForm>
<Styled.AddressEditButtonsWrapper>
Expand All @@ -120,7 +127,7 @@ export const EditableAddress = ({
</Styled.EditableFormWrapper>
)
},
[cancelEditHandler, confirmEditHandler, form, inputOnKeyDownHandler, validateAddress]
[cancelEditHandler, confirmEditHandler, form, inputOnKeyUpHandler, validateAddress]
)

const renderCustomAddressInput = useCallback(
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/components/swap/Swap.styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@ export const TargetAddressContainer = styled.div`
display: flex;
flex-direction: column;
`

export const ValueTitle = styled(UILabel).attrs({
color: 'gray',
textTransform: 'uppercase'
Expand All @@ -155,6 +154,7 @@ export const ValueTitle = styled(UILabel).attrs({
margin-right: 10px;
padding: 0;
font-size: 12px;
width: auto;
`

export const InValueLabel = styled(UILabel).attrs({
Expand Down
40 changes: 37 additions & 3 deletions src/renderer/components/swap/Swap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
assetToBase,
assetAmount
} from '@xchainjs/xchain-util'
import { Row } from 'antd'
import BigNumber from 'bignumber.js'
import * as A from 'fp-ts/Array'
import * as FP from 'fp-ts/function'
Expand All @@ -26,6 +27,7 @@ import { useIntl } from 'react-intl'
import * as RxOp from 'rxjs/operators'

import { Network } from '../../../shared/api/types'
import { WalletType } from '../../../shared/wallet/types'
import { ZERO_BASE_AMOUNT } from '../../const'
import {
getEthTokenAddress,
Expand All @@ -41,7 +43,7 @@ import { eqAsset, eqBaseAmount, eqChain, eqOAsset, eqOApproveParams } from '../.
import { sequenceSOption, sequenceTOption } from '../../helpers/fpHelpers'
import * as PoolHelpers from '../../helpers/poolHelper'
import { liveData, LiveData } from '../../helpers/rx/liveData'
import { filterWalletBalancesByAssets, getWalletBalanceByAsset } from '../../helpers/walletHelper'
import { filterWalletBalancesByAssets, getWalletBalanceByAsset, getWalletByAddress } from '../../helpers/walletHelper'
import { useSubscriptionState } from '../../hooks/useSubscriptionState'
import { swap } from '../../routes/pools'
import { ChangeSlipToleranceHandler } from '../../services/app/types'
Expand Down Expand Up @@ -85,6 +87,7 @@ import { TxModal } from '../modal/tx'
import { SwapAssets } from '../modal/tx/extra'
import { LoadingView } from '../shared/loading'
import { ViewTxButton } from '../uielements/button'
import { WalletTypeLabel } from '../uielements/common/Common.styles'
import { Fees, UIFeesRD } from '../uielements/fees'
import { Slider } from '../uielements/slider'
import { EditableAddress } from './EditableAddress'
Expand Down Expand Up @@ -156,6 +159,8 @@ export const Swap = ({
const lockedWallet = useMemo(() => isLocked(keystore) || !hasImportedKeystore(keystore), [keystore])

const [targetWalletAddress, setTargetWalletAddress] = useState<O.Option<Address>>(initialTargetWalletAddress)
const [editableTargetWalletAddress, setEditableTargetWalletAddress] =
useState<O.Option<Address>>(initialTargetWalletAddress)

const { balances: oWalletBalances, loading: walletBalancesLoading } = walletBalances

Expand Down Expand Up @@ -1180,7 +1185,11 @@ export const Swap = ({
network={network}
address={address}
onClickOpenAddress={(address) => clickAddressLinkHandler(address)}
onChangeAddress={(newAddress) => setTargetWalletAddress(O.some(newAddress))}
onChangeAddress={(newAddress) => {
setTargetWalletAddress(O.some(newAddress))
setEditableTargetWalletAddress(O.some(newAddress))
}}
onChangeEditableAddress={(newAddress) => setEditableTargetWalletAddress(O.some(newAddress))}
onChangeEditableMode={(editModeActive) => setCustomAddressEditActive(editModeActive)}
addressValidator={addressValidator}
/>
Expand All @@ -1190,6 +1199,28 @@ export const Swap = ({
[oTargetAsset, targetWalletAddress, network, addressValidator, clickAddressLinkHandler]
)

const oMatchedWalletType: O.Option<WalletType> = useMemo(
() =>
FP.pipe(
sequenceTOption(oWalletBalances, editableTargetWalletAddress),
O.chain(([walletBalances, editableTargetWalletAddress]) =>
getWalletByAddress(walletBalances, editableTargetWalletAddress)
),
O.map(({ walletType }) => walletType)
),
[oWalletBalances, editableTargetWalletAddress]
)

const renderWalletType = useMemo(() => {
return FP.pipe(
oMatchedWalletType,
O.fold(
() => <></>,
(matchedWalletType) => <WalletTypeLabel>{matchedWalletType}</WalletTypeLabel>
)
)
}, [oMatchedWalletType])

return (
<Styled.Container>
<Styled.ContentContainer>
Expand Down Expand Up @@ -1271,7 +1302,10 @@ export const Swap = ({
</Styled.ValueItemContainer>
{!lockedWallet && (
<Styled.TargetAddressContainer>
<Styled.ValueTitle>{intl.formatMessage({ id: 'swap.recipient' })}</Styled.ValueTitle>
<Row>
<Styled.ValueTitle>{intl.formatMessage({ id: 'swap.recipient' })}</Styled.ValueTitle>
{renderWalletType}
</Row>
{renderCustomAddressInput}
</Styled.TargetAddressContainer>
)}
Expand Down
44 changes: 36 additions & 8 deletions src/renderer/helpers/walletHelper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,45 @@ import * as NEA from 'fp-ts/lib/NonEmptyArray'
import * as O from 'fp-ts/lib/Option'

import { ASSETS_TESTNET } from '../../shared/mock/assets'
import { NonEmptyWalletBalances } from '../services/wallet/types'
import { NonEmptyWalletBalances, WalletBalance } from '../services/wallet/types'
import { isRuneNativeAsset } from './assetHelper'
import { eqWalletBalances } from './fp/eq'
import {
filterWalletBalancesByAssets,
getAssetAmountByAsset,
getBnbAmountFromBalances,
getLtcAmountFromBalances,
getWalletBalanceByAsset
getWalletBalanceByAsset,
getWalletByAddress
} from './walletHelper'

describe('walletHelper', () => {
const RUNE_WB = {
const RUNE_WB: WalletBalance = {
amount: baseAmount('12300000000'),
asset: AssetRuneNative,
walletAddress: 'rune native wallet address'
walletAddress: 'rune native wallet address',
walletType: 'keystore'
}
const BNB = O.fromNullable(assetFromString('BNB.BNB'))
const BOLT_WB = {
const BOLT_WB: WalletBalance = {
amount: baseAmount('23400000000'),
asset: ASSETS_TESTNET.BOLT,
walletAddress: 'bolt wallet address'
walletAddress: 'bolt wallet address',
walletType: 'keystore'
}
const BNB_WB: WalletBalance = {
amount: baseAmount('45600000000'),
asset: AssetBNB,
walletAddress: 'bnb wallet address',
walletType: 'keystore'
}
const BNB_WB = { amount: baseAmount('45600000000'), asset: AssetBNB, walletAddress: 'bnb wallet address' }

const LTC_WB = { amount: baseAmount('45300000000'), asset: AssetLTC, walletAddress: 'ltc wallet address' }
const LTC_WB: WalletBalance = {
amount: baseAmount('45300000000'),
asset: AssetLTC,
walletAddress: 'ltc wallet address',
walletType: 'keystore'
}

describe('amountByAsset', () => {
it('returns amount of RUNE', () => {
Expand Down Expand Up @@ -116,4 +130,18 @@ describe('walletHelper', () => {
expect(eqWalletBalances.equals(result, [])).toBeTruthy()
})
})

describe('getWalletByAddress', () => {
it('returns address of RUNE wallet', () => {
const balances: NonEmptyWalletBalances = NEA.fromReadonlyNonEmptyArray([RUNE_WB, BOLT_WB, BNB_WB])
const result = O.toNullable(getWalletByAddress(balances, RUNE_WB.walletAddress))
expect(isRuneNativeAsset(result?.asset ?? AssetBNB /* BNB would fail */)).toBeTruthy()
expect(result?.walletAddress).toEqual(RUNE_WB.walletAddress)
})
it('returns none if BNB wallet address is not available', () => {
const balances: NonEmptyWalletBalances = NEA.fromReadonlyNonEmptyArray([BOLT_WB, BNB_WB])
const result = getWalletByAddress(balances, RUNE_WB.walletAddress)
expect(result).toBeNone()
})
})
})
6 changes: 6 additions & 0 deletions src/renderer/helpers/walletHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,9 @@ export const addressFromWalletAddress = ({ address }: Pick<WalletAddress, 'addre
export const addressFromOptionalWalletAddress = (
oWalletAddress: O.Option<Pick<WalletAddress, 'address'>>
): O.Option<Address> => FP.pipe(oWalletAddress, O.map(addressFromWalletAddress))

export const getWalletByAddress = (walletBalances: NonEmptyWalletBalances, address: Address): O.Option<WalletBalance> =>
FP.pipe(
walletBalances,
A.findFirst(({ walletAddress }) => eqAddress.equals(walletAddress, address))
)

0 comments on commit 97d8a95

Please sign in to comment.