Skip to content

Commit

Permalink
fixes(swap) fees calculation, slippage error, market price (#2694)
Browse files Browse the repository at this point in the history
  • Loading branch information
stackchain authored Sep 21, 2023
2 parents 9338405 + 27006a2 commit 81f8582
Show file tree
Hide file tree
Showing 22 changed files with 746 additions and 172 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import LinearGradient from 'react-native-linear-gradient'

import {Spacer} from '../../../../../components'
import {useMetrics} from '../../../../../metrics/metricsManager'
import {useSelectedWallet} from '../../../../../SelectedWallet'
import {COLORS} from '../../../../../theme'
import {useNavigateTo} from '../../navigation'
import {PoolIcon} from '../../PoolIcon/PoolIcon'
Expand All @@ -17,6 +18,7 @@ type Props = {
}
export const SelectPoolFromList = ({data = []}: Props) => {
const strings = useStrings()
const wallet = useSelectedWallet()
const {selectedPoolChanged, createOrder} = useSwap()
const {poolTouched} = useSwapTouched()
const [selectedCardIndex, setSelectedCardIndex] = useState(createOrder.selectedPool?.poolId)
Expand Down Expand Up @@ -60,15 +62,15 @@ export const SelectPoolFromList = ({data = []}: Props) => {
<View style={styles.info}>
<Text style={styles.infoLabel}>{strings.price}</Text>

<Text style={styles.infoValue}>{pool.price} ADA</Text>
<Text style={styles.infoValue}>{`${pool.price} ${wallet.primaryTokenInfo.ticker}`}</Text>
</View>
</View>

<View>
<Spacer height={8} />

<View style={styles.info}>
<Text style={styles.infoLabel}>{strings.tvl}, ADA</Text>
<Text style={styles.infoLabel}>{`${strings.tvl}, ${wallet.primaryTokenInfo.ticker}`}</Text>

<Text style={styles.infoValue}>{pool.deposit.quantity}</Text>
</View>
Expand All @@ -88,7 +90,7 @@ export const SelectPoolFromList = ({data = []}: Props) => {
<Spacer height={8} />

<View style={styles.info}>
<Text style={styles.infoLabel}>{strings.batcherFee}, ADA</Text>
<Text style={styles.infoLabel}>{`${strings.batcherFee}, ${wallet.primaryTokenInfo.ticker}`}</Text>

<Text style={styles.infoValue}>{pool.batcherFee.quantity}</Text>
</View>
Expand Down
2 changes: 1 addition & 1 deletion apps/wallet-mobile/src/features/Swap/common/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ export const messages = defineMessages({
},
swapFeesTitle: {
id: 'swap.swapScreen.swapFeesTitle',
defaultMessage: `!!!Fee`,
defaultMessage: `!!!Fees`,
},
swapMinReceived: {
id: 'swap.swapScreen.swapMinReceived',
Expand Down
5 changes: 4 additions & 1 deletion apps/wallet-mobile/src/features/Swap/common/useSwapTx.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {Datum} from '@emurgo/yoroi-lib'
import {useSwap} from '@yoroi/swap'
import {omit} from 'lodash'
import {UseMutationOptions} from 'react-query'

import {useSelectedWallet} from '../../../SelectedWallet'
Expand All @@ -8,12 +9,14 @@ import {YoroiEntry, YoroiUnsignedTx} from '../../../yoroi-wallets/types'

export const useSwapTx = (options?: UseMutationOptions<YoroiUnsignedTx, Error, {entry: YoroiEntry; datum: Datum}>) => {
const {createOrder} = useSwap()
const orderMetadata = omit(createOrder, ['datumHash', 'datum'])
const metadata = [
{
label: '674',
data: {msg: [`${createOrder.selectedPool?.provider}: Swap B for A Order Request`]},
data: {msg: [`${createOrder.selectedPool?.provider}: Swap B for A Order Request`], order: {...orderMetadata}},
},
]

const wallet = useSelectedWallet()
const mutation = useMutationWithInvalidations({
mutationFn: (data) => wallet.createUnsignedTx(data.entry, metadata, data.datum),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {useSwap} from '@yoroi/swap'
import {getMinAdaReceiveAfterSlippage, getTotalFees, useSwap} from '@yoroi/swap'
import React from 'react'
import {StyleSheet, TouchableOpacity, View} from 'react-native'

import {Icon, Spacer, Text} from '../../../../components'
import {AmountItem} from '../../../../components/AmountItem/AmountItem'
import {BottomSheetModal} from '../../../../components/BottomSheetModal'
import {useLanguage} from '../../../../i18n'
import {useSelectedWallet} from '../../../../SelectedWallet'
import {COLORS} from '../../../../theme'
import {useTokenInfo} from '../../../../yoroi-wallets/hooks'
Expand All @@ -19,6 +20,7 @@ export const TransactionSummary = () => {
})
const strings = useStrings()
const wallet = useSelectedWallet()
const {numberLocale} = useLanguage()
const {createOrder, unsignedTx} = useSwap()
const {amounts, selectedPool} = createOrder

Expand All @@ -30,21 +32,32 @@ export const TransactionSummary = () => {
asQuantity(Number(Object.values(unsignedTx?.fee))),
Number(wallet.primaryTokenInfo.decimals),
)

const feesInfo = [
{
label: strings.swapMinAdaTitle,
value: `${Quantities.denominated(selectedPool.deposit.quantity, Number(wallet.primaryTokenInfo.decimals))} ADA`,
value: `${Quantities.denominated(selectedPool.deposit.quantity, Number(wallet.primaryTokenInfo.decimals))} ${
wallet.primaryTokenInfo.ticker
}`,
info: strings.swapMinAda,
},
{
label: strings.swapMinReceivedTitle,
value: '?', // TODO add real value
value: `${getMinAdaReceiveAfterSlippage(
amounts.buy.quantity,
createOrder.slippage,
buyTokenInfo.decimals ?? 0,
numberLocale,
)} ${tokenToBuyName}`,
info: strings.swapMinReceived,
},
{
label: strings.swapFeesTitle,
value: `${poolFee} ADA`,
value: `${getTotalFees(
selectedPool?.batcherFee.quantity,
asQuantity(poolFee),
wallet.primaryTokenInfo.decimals ?? 0,
numberLocale,
)} ${wallet.primaryTokenInfo.ticker}`,
info: strings.swapFees,
},
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {ScrollView} from 'react-native-gesture-handler'
import {Button, Spacer} from '../../../../../components'
import {LoadingOverlay} from '../../../../../components/LoadingOverlay'
import {useMetrics} from '../../../../../metrics/metricsManager'
import {useAddresses} from '../../../../../Receive/Addresses'
import {useSelectedWallet} from '../../../../../SelectedWallet'
import {COLORS} from '../../../../../theme'
import {useTokenInfos} from '../../../../../yoroi-wallets/hooks'
Expand All @@ -35,7 +34,6 @@ export const CreateOrder = () => {
const {createOrder, selectedPoolChanged, unsignedTxChanged, txPayloadChanged} = useSwap()
const wallet = useSelectedWallet()
const {track} = useMetrics()
const addresses = useAddresses()

const tokenInfos = useTokenInfos({
wallet,
Expand Down Expand Up @@ -115,8 +113,8 @@ export const CreateOrder = () => {
buy: amounts.buy,
pools: poolList,
selectedPool: createOrder.selectedPool,
slippage: createOrder.slippage,
address: addresses.used[0],
slippage: createOrder.slippage * 100,
address: wallet.externalAddresses[0],
}

if (orderDetails.pools === undefined) return
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
/* eslint-disable react/jsx-newline */
import {useSwap} from '@yoroi/swap'
import BigNumber from 'bignumber.js'
import * as React from 'react'
import {StyleSheet, Text, TextInput, View} from 'react-native'

import {useLanguage} from '../../../../../i18n'
import {useSelectedWallet} from '../../../../../SelectedWallet'
import {COLORS} from '../../../../../theme'
import {useTokenInfo} from '../../../../../yoroi-wallets/hooks'
import {Quantities} from '../../../../../yoroi-wallets/utils'
import {asQuantity, Quantities} from '../../../../../yoroi-wallets/utils'
import {getBuyQuantityForLimitOrder} from '../../../common/helpers'
import {useStrings} from '../../../common/strings'
import {useSwapTouched} from '../../../common/SwapFormProvider'

const BORDER_SIZE = 1
const PRECISION = 10

export const EditLimitPrice = () => {
const strings = useStrings()
const {numberLocale} = useLanguage()
const [text, setText] = React.useState('')

const wallet = useSelectedWallet()

const {createOrder, limitPriceChanged, buyAmountChanged} = useSwap()
Expand All @@ -42,29 +40,30 @@ export const EditLimitPrice = () => {
: 0

limitPriceChanged(`${defaultPrice}`)
const formattedValue = BigNumber(defaultPrice).toFormat(numberLocale)
setText(formattedValue)

const sellQuantityDenominated = Quantities.denominated(
createOrder.amounts.sell.quantity,
sellTokenInfo.decimals ?? 0,
)
buyAmountChanged({
quantity: getBuyQuantityForLimitOrder(sellQuantityDenominated, `${defaultPrice}`, buyTokenInfo.decimals ?? 0),
quantity: getBuyQuantityForLimitOrder(
sellQuantityDenominated,
asQuantity(defaultPrice ?? 0),
buyTokenInfo.decimals ?? 0,
),
tokenId: createOrder.amounts.buy.tokenId,
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isBuyTouched, isSellTouched, sellTokenInfo.id, buyTokenInfo.id, createOrder?.selectedPool?.price, numberLocale])

const onChange = (text: string) => {
const [newText, quantity] = Quantities.parseFromText(text, PRECISION, numberLocale)
const [_, quantity] = Quantities.parseFromText(text, PRECISION, numberLocale)
const value = Quantities.denominated(quantity, PRECISION)
const sellQuantityDenominated = Quantities.denominated(
createOrder.amounts.sell.quantity,
sellTokenInfo.decimals ?? 0,
)
limitPriceChanged(value)
setText(newText)
buyAmountChanged({
quantity: getBuyQuantityForLimitOrder(sellQuantityDenominated, value, buyTokenInfo.decimals ?? 0),
tokenId: createOrder.amounts.buy.tokenId,
Expand All @@ -76,7 +75,7 @@ export const EditLimitPrice = () => {
<Text style={styles.label}>{disabled ? strings.marketPrice : strings.limitPrice}</Text>

<View style={styles.content}>
<AmountInput onChange={onChange} value={text} editable={!disabled} />
<AmountInput onChange={onChange} value={createOrder.limitPrice} editable={!disabled} />

<View style={[styles.textWrapper, disabled && styles.disabled]}>
<Text style={styles.text}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
import {useSwap} from '@yoroi/swap'
import {getMinAdaReceiveAfterSlippage, getTotalFees, useSwap} from '@yoroi/swap'
import {capitalize} from 'lodash'
import React from 'react'
import {StyleSheet, Text, TouchableOpacity, View} from 'react-native'

import {
BottomSheetModal,
ExpandableInfoCard,
HeaderWrapper,
HiddenInfoWrapper,
MainInfoWrapper,
Spacer,
} from '../../../../../../components'
import {useLanguage} from '../../../../../../i18n'
import {useSelectedWallet} from '../../../../../../SelectedWallet'
import {useTokenInfo} from '../../../../../../yoroi-wallets/hooks'
import {Quantities} from '../../../../../../yoroi-wallets/utils'
import {asQuantity, Quantities} from '../../../../../../yoroi-wallets/utils'
import {useNavigateTo} from '../../../../common/navigation'
import {PoolIcon} from '../../../../common/PoolIcon/PoolIcon'
import {useStrings} from '../../../../common/strings'
import {useSwapTouched} from '../../../../common/SwapFormProvider'

export const ShowPoolActions = () => {
const navigateTo = useNavigateTo()
const {numberLocale} = useLanguage()
const {createOrder} = useSwap()
const strings = useStrings()
const {isBuyTouched, isSellTouched, isPoolTouched} = useSwapTouched()
Expand All @@ -33,9 +37,8 @@ export const ShowPoolActions = () => {
return <></>
}
const totalAmount = Quantities.format(amounts.buy.quantity, buyTokenInfo.decimals ?? 0)
const protocolCapitalize = selectedPool.provider[0].toUpperCase() + selectedPool.provider.substring(1)
const calculatedFee = (Number(selectedPool?.fee) / 100) * Number(createOrder.amounts.sell.quantity)
const poolFee = Quantities.format(`${calculatedFee}`, sellTokenInfo.decimals ?? 0)
const providerFee = Quantities.format(asQuantity(calculatedFee ?? 0), sellTokenInfo.decimals ?? 0)

const id = selectedPool.poolId
const extended = id === hiddenInfoOpenId
Expand All @@ -54,14 +57,26 @@ export const ShowPoolActions = () => {

<Spacer width={10} />

<Text>{`${protocolCapitalize}${isPoolTouched ? '' : ` ${strings.autoPool}`}`}</Text>
<Text>{`${capitalize(selectedPool.provider)}${isPoolTouched ? '' : ` ${strings.autoPool}`}`}</Text>
</View>
</Header>
}
adornment={
<HiddenInfo
poolFee={poolFee}
deposit={Quantities.denominated(selectedPool.deposit.quantity, Number(wallet.primaryTokenInfo.decimals))}
totalFees={getTotalFees(
selectedPool?.batcherFee.quantity,
asQuantity(providerFee ?? 0),
wallet.primaryTokenInfo.decimals ?? 0,
numberLocale,
)}
minReceived={getMinAdaReceiveAfterSlippage(
amounts.buy.quantity,
createOrder.slippage,
buyTokenInfo.decimals ?? 0,
numberLocale,
)}
minAda={Quantities.denominated(selectedPool.deposit.quantity, Number(wallet.primaryTokenInfo.decimals))}
buyTokenName={tokenName}
/>
}
extended={extended}
Expand Down Expand Up @@ -89,29 +104,68 @@ const Header = ({
)
}

const HiddenInfo = ({poolFee, deposit}: {poolFee: string; deposit: string}) => {
const HiddenInfo = ({
totalFees,
minAda,
minReceived,
buyTokenName,
}: {
totalFees: string
minAda: string
minReceived: string
buyTokenName: string
}) => {
const [bottomSheetState, setBottomSheetSate] = React.useState<{isOpen: boolean; title: string; content?: string}>({
isOpen: false,
title: '',
content: '',
})
const strings = useStrings()
const wallet = useSelectedWallet()

return (
<View>
{[
{
label: strings.swapMinAdaTitle,
value: deposit,
value: `${minAda} ${wallet.primaryTokenInfo.ticker}`,
info: strings.swapMinAda,
},
{
label: strings.swapMinReceivedTitle,
value: '?', // TODO add real value
value: `${minReceived} ${buyTokenName}`,
info: strings.swapMinReceived,
},
{
label: strings.swapFeesTitle,
value: String(poolFee),
value: `${totalFees} ${wallet.primaryTokenInfo.ticker}`,
info: strings.swapFees,
},
].map((item) => (
<HiddenInfoWrapper key={item.label} value={item.value} label={item.label} info={item.info} />
<HiddenInfoWrapper
key={item.label}
value={item.value}
label={item.label}
info={item.info}
onPress={() => {
setBottomSheetSate({
isOpen: true,
title: item.label,
content: item.info,
})
}}
/>
))}

<BottomSheetModal
isOpen={bottomSheetState.isOpen}
title={bottomSheetState.title}
onClose={() => {
setBottomSheetSate({isOpen: false, title: '', content: ''})
}}
>
<Text style={styles.text}>{bottomSheetState.content}</Text>
</BottomSheetModal>
</View>
)
}
Expand All @@ -130,4 +184,11 @@ const MainInfo = ({totalAmount, tokenName}: {totalAmount: string; tokenName: str

const styles = StyleSheet.create({
flex: {flexDirection: 'row', alignItems: 'center'},
text: {
textAlign: 'left',
fontSize: 16,
lineHeight: 24,
fontWeight: '400',
color: '#242838',
},
})
Loading

0 comments on commit 81f8582

Please sign in to comment.