Merge pull request #13 from blockworks-foundation/sort-sell-token-list

sort sell token list by max amount
This commit is contained in:
saml33 2022-09-20 15:01:19 +10:00 committed by GitHub
commit ae5f95ac35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 158 additions and 28 deletions

View File

@ -1,7 +1,7 @@
import Decimal from 'decimal.js' import Decimal from 'decimal.js'
import { ChangeEvent, useEffect, useRef, useState } from 'react' import { ChangeEvent, useEffect, useMemo, useRef, useState } from 'react'
import mangoStore from '@store/mangoStore' import mangoStore from '../../store/mangoStore'
import { useTokenMax } from './useTokenMax' import { getTokenInMax } from './useTokenMax'
const LeverageSlider = ({ const LeverageSlider = ({
amount, amount,
@ -74,7 +74,17 @@ export const SwapLeverageSlider = ({
onChange: (x: string) => void onChange: (x: string) => void
}) => { }) => {
const mangoAccount = mangoStore((s) => s.mangoAccount.current) const mangoAccount = mangoStore((s) => s.mangoAccount.current)
const { amountWithBorrow } = useTokenMax() const inputTokenInfo = mangoStore((s) => s.swap.inputTokenInfo)
const { amountWithBorrow } = useMemo(() => {
const group = mangoStore.getState().group
if (inputTokenInfo && group) {
return getTokenInMax(inputTokenInfo.address, group, true)
}
return {
amountWithBorrow: new Decimal(0),
}
}, [inputTokenInfo])
return ( return (
<> <>

View File

@ -33,7 +33,7 @@ import {
INPUT_TOKEN_DEFAULT, INPUT_TOKEN_DEFAULT,
OUTPUT_TOKEN_DEFAULT, OUTPUT_TOKEN_DEFAULT,
} from '../../utils/constants' } from '../../utils/constants'
import { useTokenMax } from './useTokenMax' import { getTokenInMax } from './useTokenMax'
import WalletIcon from '../icons/WalletIcon' import WalletIcon from '../icons/WalletIcon'
const MAX_DIGITS = 11 const MAX_DIGITS = 11
@ -61,11 +61,22 @@ const SwapForm = () => {
const jupiterTokens = mangoStore((s) => s.jupiterTokens) const jupiterTokens = mangoStore((s) => s.jupiterTokens)
const connected = mangoStore((s) => s.connected) const connected = mangoStore((s) => s.connected)
const [debouncedAmountIn] = useDebounce(amountInFormValue, 300) const [debouncedAmountIn] = useDebounce(amountInFormValue, 300)
const { const {
amount: tokenMax, amount: tokenMax,
amountWithBorrow, amountWithBorrow,
decimals, decimals,
} = useTokenMax(useMargin) } = useMemo(() => {
const group = mangoStore.getState().group
if (inputTokenInfo && group) {
return getTokenInMax(inputTokenInfo.address, group, useMargin)
}
return {
amount: new Decimal(0),
amountWithBorrow: new Decimal(0),
decimals: 6,
}
}, [inputTokenInfo, useMargin])
const amountIn: Decimal | null = useMemo(() => { const amountIn: Decimal | null = useMemo(() => {
return Number(debouncedAmountIn) return Number(debouncedAmountIn)
@ -234,6 +245,7 @@ const SwapForm = () => {
: handleTokenOutSelect : handleTokenOutSelect
} }
type={showTokenSelect} type={showTokenSelect}
useMargin={useMargin}
/> />
</EnterBottomExitBottom> </EnterBottomExitBottom>
<EnterBottomExitBottom <EnterBottomExitBottom

View File

@ -10,6 +10,9 @@ import {
XMarkIcon, XMarkIcon,
} from '@heroicons/react/20/solid' } from '@heroicons/react/20/solid'
import { useTranslation } from 'next-i18next' import { useTranslation } from 'next-i18next'
import { floorToDecimal } from '../../utils/numbers'
import Decimal from 'decimal.js'
import { getTokenInMax } from './useTokenMax'
const generateSearchTerm = (item: Token, searchValue: string) => { const generateSearchTerm = (item: Token, searchValue: string) => {
const normalizedSearchValue = searchValue.toLowerCase() const normalizedSearchValue = searchValue.toLowerCase()
@ -40,17 +43,27 @@ const startSearch = (items: Token[], searchValue: string) => {
const TokenItem = ({ const TokenItem = ({
token, token,
onSubmit, onSubmit,
useMargin,
type,
}: { }: {
token: Token token: Token
onSubmit: (x: string) => void onSubmit: (x: string) => void
useMargin: boolean
type: string
}) => { }) => {
const { address, symbol, logoURI, name } = token const { address, symbol, logoURI, name } = token
const isDisabled =
(type === 'input' && useMargin && token.amountWithBorrow!.eq(0)) ||
(type === 'input' && !useMargin && token.amount!.eq(0))
return ( return (
<div> <div>
<button <button
key={address} key={address}
className="flex w-full cursor-pointer items-center justify-between rounded-md p-2 font-normal focus:bg-th-bkg-3 focus:outline-none md:hover:bg-th-bkg-2" className="flex w-full cursor-pointer items-center justify-between rounded-md p-2 font-normal focus:bg-th-bkg-3 focus:outline-none md:hover:bg-th-bkg-2"
onClick={() => onSubmit(address)} onClick={() => onSubmit(address)}
disabled={isDisabled}
> >
<div className="flex items-center"> <div className="flex items-center">
<picture> <picture>
@ -59,9 +72,18 @@ const TokenItem = ({
</picture> </picture>
<div className="ml-2.5"> <div className="ml-2.5">
<div className="text-left text-th-fgd-2">{symbol || 'unknown'}</div> <div className="text-left text-th-fgd-2">{symbol || 'unknown'}</div>
<div className="text-left text-th-fgd-4">{name || 'unknown'}</div> <div className="text-left text-xs text-th-fgd-4">
{name || 'unknown'}
</div> </div>
</div> </div>
</div>
{type === 'input' ? (
<p className="text-sm text-th-fgd-2">
{useMargin
? token.amountWithBorrow!.toString()
: token.amount!.toString()}
</p>
) : null}
</button> </button>
</div> </div>
) )
@ -73,10 +95,12 @@ const SwapFormTokenList = ({
onClose, onClose,
onTokenSelect, onTokenSelect,
type, type,
useMargin,
}: { }: {
onClose: () => void onClose: () => void
onTokenSelect: (x: string) => void onTokenSelect: (x: string) => void
type: string type: string
useMargin: boolean
}) => { }) => {
const { t } = useTranslation(['common', 'swap']) const { t } = useTranslation(['common', 'swap'])
const [search, setSearch] = useState('') const [search, setSearch] = useState('')
@ -85,6 +109,8 @@ const SwapFormTokenList = ({
const jupiterTokens = mangoStore((s) => s.jupiterTokens) const jupiterTokens = mangoStore((s) => s.jupiterTokens)
const inputBank = mangoStore((s) => s.swap.inputBank) const inputBank = mangoStore((s) => s.swap.inputBank)
const outputBank = mangoStore((s) => s.swap.outputBank) const outputBank = mangoStore((s) => s.swap.outputBank)
const mangoAccount = mangoStore((s) => s.mangoAccount.current)
const group = mangoStore((s) => s.group)
const popularTokens = useMemo(() => { const popularTokens = useMemo(() => {
return tokens.filter((token) => { return tokens.filter((token) => {
@ -105,27 +131,39 @@ const SwapFormTokenList = ({
}, [onClose]) }, [onClose])
const tokenInfos = useMemo(() => { const tokenInfos = useMemo(() => {
if (tokens?.length) { if (
const filteredTokens = tokens.filter((token) => { tokens?.length &&
if (type === 'input') { group &&
return token.symbol === outputBank?.name ? false : true mangoAccount &&
} else { outputBank &&
return token.symbol === inputBank?.name ? false : true type === 'input'
} ) {
const filteredSortedTokens = tokens
.map((token) => {
const max = getTokenInMax(token.address, group, useMargin)
return { ...token, ...max }
}) })
if (walletTokens?.length) { .filter((token) => (token.symbol === outputBank?.name ? false : true))
const walletMints = walletTokens.map((tok) => tok.mint.toString()) .sort((a, b) =>
return filteredTokens.sort( useMargin
(a, b) => ? Number(b.amountWithBorrow) - Number(a.amountWithBorrow)
walletMints.indexOf(b.address) - walletMints.indexOf(a.address) : Number(b.amount) - Number(a.amount)
) )
} else {
return filteredSortedTokens
} else if (tokens?.length) {
const filteredTokens = tokens
.map((token) => ({
...token,
amount: new Decimal(0),
amountWithBorrow: new Decimal(0),
}))
.filter((token) => (token.symbol === inputBank?.name ? false : true))
return filteredTokens return filteredTokens
}
} else { } else {
return [] return []
} }
}, [tokens, walletTokens, inputBank, outputBank]) }, [tokens, walletTokens, inputBank, outputBank, mangoAccount, group])
const handleUpdateSearch = (e: ChangeEvent<HTMLInputElement>) => { const handleUpdateSearch = (e: ChangeEvent<HTMLInputElement>) => {
setSearch(e.target.value) setSearch(e.target.value)
@ -143,7 +181,9 @@ const SwapFormTokenList = ({
<IconButton className="absolute top-2 right-2" onClick={onClose} hideBg> <IconButton className="absolute top-2 right-2" onClick={onClose} hideBg>
<XMarkIcon className="h-5 w-5" /> <XMarkIcon className="h-5 w-5" />
</IconButton> </IconButton>
<div className="flex items-center text-th-fgd-4"> {/* No need for search/popular tokens until we have more tokens */}
{/* <div className="flex items-center text-th-fgd-4">
<Input <Input
type="text" type="text"
placeholder="Search by token or paste address" placeholder="Search by token or paste address"
@ -184,14 +224,22 @@ const SwapFormTokenList = ({
) )
})} })}
</div> </div>
) : null} */}
{/* <div className="my-2 border-t border-th-bkg-4"></div> */}
<div className="mb-2 flex justify-between rounded bg-th-bkg-3 p-2">
<p className="text-xs text-th-fgd-4">{t('token')}</p>
{type === 'input' ? (
<p className="text-xs text-th-fgd-4">{t('max')}</p>
) : null} ) : null}
<div className="my-2 border-t border-th-bkg-4"></div> </div>
<div className="mb-2 overflow-auto"> <div className="overflow-auto">
{sortedTokens.map((token) => ( {sortedTokens.map((token) => (
<TokenItem <TokenItem
key={token.address} key={token.address}
token={token} token={token}
onSubmit={onTokenSelect} onSubmit={onTokenSelect}
useMargin={useMargin}
type={type}
/> />
))} ))}
</div> </div>

View File

@ -22,6 +22,63 @@ export const getMaxWithdrawForBank = (
: Decimal.min(accountBalance, vaultBalance, maxBorrow!) : Decimal.min(accountBalance, vaultBalance, maxBorrow!)
} }
export const getTokenInMax = (
inputTokenAddress: string,
group: Group,
useMargin: boolean
) => {
const mangoAccount = mangoStore.getState().mangoAccount.current
const outputBank = mangoStore.getState().swap.outputBank
const inputBank = group.banksMapByMint.get(inputTokenAddress)![0]
if (!group || !inputBank || !mangoAccount || !outputBank) {
return {
amount: new Decimal(0.0),
decimals: 6,
amountWithBorrow: new Decimal(0.0),
}
}
const inputTokenBalance = floorToDecimal(
mangoAccount.getTokenBalanceUi(inputBank),
inputBank.mintDecimals
)
const maxAmountWithoutMargin = inputTokenBalance.gt(0)
? inputTokenBalance
: new Decimal(0)
const maxUiAmountWithBorrow = floorToDecimal(
mangoAccount?.getMaxSourceUiForTokenSwap(
group,
inputBank.mint,
outputBank.mint,
1
)!,
inputBank.mintDecimals
)
const inputBankVaultBalance = group.getTokenVaultBalanceByMintUi(
inputBank.mint
)
const maxAmount = useMargin
? Decimal.min(
maxAmountWithoutMargin,
inputBankVaultBalance,
maxUiAmountWithBorrow!
)
: Decimal.min(maxAmountWithoutMargin, inputBankVaultBalance)
const maxAmountWithBorrow = Decimal.min(
maxUiAmountWithBorrow!,
inputBankVaultBalance
)
return {
amount: maxAmount,
amountWithBorrow: maxAmountWithBorrow,
decimals: inputBank.mintDecimals,
}
}
export const useTokenMax = (useMargin = true) => { export const useTokenMax = (useMargin = true) => {
const { mangoAccount } = useMangoAccount() const { mangoAccount } = useMangoAccount()
const inputBank = mangoStore((s) => s.swap.inputBank) const inputBank = mangoStore((s) => s.swap.inputBank)

View File

@ -1,4 +1,5 @@
import { RouteInfo } from '@jup-ag/core' import { RouteInfo } from '@jup-ag/core'
import Decimal from 'decimal.js'
export type Routes = { export type Routes = {
routesInfos: RouteInfo[] routesInfos: RouteInfo[]
@ -16,4 +17,6 @@ export interface Token {
extensions?: { extensions?: {
coingeckoId: string coingeckoId: string
} }
amount?: Decimal
amountWithBorrow?: Decimal
} }