Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixes(swap) fees calculation, slippage error, market price #2694

Merged
merged 14 commits into from
Sep 21, 2023
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,
stackchain marked this conversation as resolved.
Show resolved Hide resolved
),
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