diff --git a/examples/asset-list/components/wallet/WalletConnect.tsx b/examples/asset-list/components/wallet/WalletConnect.tsx index a74fe24db..a26bb1105 100644 --- a/examples/asset-list/components/wallet/WalletConnect.tsx +++ b/examples/asset-list/components/wallet/WalletConnect.tsx @@ -55,10 +55,7 @@ export const ConnectWalletButton = ({ gap="$4" as="span" borderRadius="8px" - // @ts-ignore - style={{ - zIndex: '1', - }} + zIndex={1} > diff --git a/examples/stake-tokens/components/staking/Overview.tsx b/examples/stake-tokens/components/staking/Overview.tsx index 26444017d..db875b19f 100644 --- a/examples/stake-tokens/components/staking/Overview.tsx +++ b/examples/stake-tokens/components/staking/Overview.tsx @@ -82,11 +82,11 @@ const Overview = ({ diff --git a/examples/stake-tokens/components/wallet/WalletConnect.tsx b/examples/stake-tokens/components/wallet/WalletConnect.tsx index b669953cb..a70e783b4 100644 --- a/examples/stake-tokens/components/wallet/WalletConnect.tsx +++ b/examples/stake-tokens/components/wallet/WalletConnect.tsx @@ -55,17 +55,16 @@ export const ConnectWalletButton = ({ gap="$4" as="span" borderRadius="8px" - // TODO fix type error - // @ts-ignore - style={{ - zIndex: '1', - }} + zIndex={1} + color="$white" > - + - {buttonText ? buttonText : 'Connect Wallet'} + + {buttonText ? buttonText : 'Connect Wallet'} + ); diff --git a/examples/swap-tokens/components/Main.tsx b/examples/swap-tokens/components/Main.tsx index a8efb6625..bc011bf98 100644 --- a/examples/swap-tokens/components/Main.tsx +++ b/examples/swap-tokens/components/Main.tsx @@ -1,17 +1,29 @@ import { Box, Text } from '@interchain-ui/react'; -import { useSwap } from "@/hooks"; +import { useSwap } from '@/hooks'; import { Swap } from '@/components'; export function Main() { const { - to, setTo, - from, setFrom, - swap, info, - steps, tokens, amount, - slippage, setSlippage, - onFlip, onSwap, onAmountChange, - isLoading, isSwapping, isRoutesEmpty, - isSwapDisabled, isWalletConnected, isInsufficientBalance + to, + setTo, + from, + setFrom, + swap, + info, + steps, + tokens, + amount, + slippage, + setSlippage, + onFlip, + onSwap, + onAmountChange, + isLoading, + isSwapping, + isRoutesEmpty, + isSwapDisabled, + isWalletConnected, + isInsufficientBalance, } = useSwap(); function buttonText() { @@ -21,32 +33,32 @@ export function Main() { return 'Swap'; } - return ( - isWalletConnected ? - : - - - Please connect to your wallet. - - + return isWalletConnected ? ( + + ) : ( + + + Please connect to your wallet. + + ); -} \ No newline at end of file +} diff --git a/examples/swap-tokens/components/swap/Swap.tsx b/examples/swap-tokens/components/swap/Swap.tsx index d957e25b5..a2f1fc161 100644 --- a/examples/swap-tokens/components/swap/Swap.tsx +++ b/examples/swap-tokens/components/swap/Swap.tsx @@ -1,32 +1,37 @@ import BigNumber from 'bignumber.js'; import { Box, Divider, Spinner, useColorModeValue } from '@interchain-ui/react'; -import { Swap } from '@/hooks'; +import { SwapDef } from '@/hooks'; import { - Token, TokenList, - SwapButton, SwapFromTo, SwapDetails, - SwapSlippage, SwapInfoProps, SwapRouteStep + Token, + TokenList, + SwapButton, + SwapFromTo, + SwapDetails, + SwapSlippage, + SwapInfoProps, + SwapRouteStep, } from '.'; export type SwapProps = { - from?: Token, - to?: Token, - swap: Swap, - info: SwapInfoProps - steps: SwapRouteStep[] - tokens: TokenList - amount?: string - loading?: boolean - swapping?: boolean - slippage?: number - buttonText?: string - buttonDisabled?: boolean - onFlip?: () => void - onToChange?: (token: Token) => void - onFromChange?: (token: Token) => void - onSwapButtonClick?: () => void - onAmountChange?: (amount: string) => void - onSlippageChange?: (tolerance: number) => void -} + from?: Token; + to?: Token; + swap: SwapDef; + info: SwapInfoProps; + steps: SwapRouteStep[]; + tokens: TokenList; + amount?: string; + loading?: boolean; + swapping?: boolean; + slippage?: number; + buttonText?: string; + buttonDisabled?: boolean; + onFlip?: () => void; + onToChange?: (token: Token) => void; + onFromChange?: (token: Token) => void; + onSwapButtonClick?: () => void; + onAmountChange?: (amount: string) => void; + onSlippageChange?: (tolerance: number) => void; +}; export function Swap({ from = {} as Token, @@ -43,16 +48,21 @@ export function Swap({ buttonDisabled = false, onFlip = () => {}, onToChange = () => {}, - onFromChange = () => {}, + onFromChange = () => {}, onAmountChange = () => {}, onSwapButtonClick = () => {}, onSlippageChange = () => {}, }: SwapProps) { - const isDetailsExpandable = new BigNumber(amount).gt(0) && Boolean(info); return ( - + - - + + + - + ); -} \ No newline at end of file +} diff --git a/examples/swap-tokens/components/swap/SwapButton.tsx b/examples/swap-tokens/components/swap/SwapButton.tsx index 1e9337f79..3a2c85e1b 100644 --- a/examples/swap-tokens/components/swap/SwapButton.tsx +++ b/examples/swap-tokens/components/swap/SwapButton.tsx @@ -1,10 +1,10 @@ -import { Button } from '@interchain-ui/react'; +import { Button, useColorModeValue } from '@interchain-ui/react'; export type SwapButtonProps = { - text?: string - disabled?: boolean - onClick?: () => void -} + text?: string; + disabled?: boolean; + onClick?: () => void; +}; export function SwapButton({ text = 'Swap', @@ -17,9 +17,11 @@ export function SwapButton({ intent="tertiary" onClick={onClick} disabled={disabled} - attributes={{ width: "$full" }} + attributes={{ + width: '$full', + }} > - {text} + {text} - ) -} \ No newline at end of file + ); +} diff --git a/examples/swap-tokens/components/swap/SwapFromTo.tsx b/examples/swap-tokens/components/swap/SwapFromTo.tsx index fcc552873..87f15c20b 100644 --- a/examples/swap-tokens/components/swap/SwapFromTo.tsx +++ b/examples/swap-tokens/components/swap/SwapFromTo.tsx @@ -1,23 +1,43 @@ import { useState } from 'react'; -import { Box, Stack, IconButton, IconName, useColorModeValue } from '@interchain-ui/react'; +import { + Box, + Stack, + IconButton, + IconName, + useColorModeValue, +} from '@interchain-ui/react'; import { Token, TokenList, SwapTokenInput } from '.'; -import { Swap } from '@/hooks'; +import { SwapDef } from '@/hooks'; export type SwapFromToProps = { - from: Token - to: Token - swap: Swap - tokens: TokenList - amount?: string - onFlip?: () => void - onToChange?: (token: Token) => void - onFromChange?: (token: Token) => void - onAmountChange?: (amount: string) => void -} + from: Token; + to: Token; + swap: SwapDef; + tokens: TokenList; + amount?: string; + onFlip?: () => void; + onToChange?: (token: Token) => void; + onFromChange?: (token: Token) => void; + onAmountChange?: (amount: string) => void; +}; -export function SwapFromTo({ from, to, swap, tokens, amount = '0', onFlip, onToChange, onFromChange, onAmountChange = () => {} }: SwapFromToProps) { +export function SwapFromTo({ + from, + to, + swap, + tokens, + amount = '0', + onFlip, + onToChange, + onFromChange, + onAmountChange = () => {}, +}: SwapFromToProps) { return ( - + onAmountChange(String(Number(from.amount) / 2))} + onHalfButtonClick={() => + onAmountChange(String(Number(from.amount) / 2)) + } onMaxButtonClick={() => onAmountChange(String(from.amount))} onTokenSelected={onFromChange} onAmountChange={onAmountChange} @@ -42,21 +64,27 @@ export function SwapFromTo({ from, to, swap, tokens, amount = '0', onFlip, onToC disabled={true} onTokenSelected={onToChange} /> - + + - ) + ); } export type SwapSwitchProps = { - onClick?: () => void -} + onClick?: () => void; +}; + +export function SwapSwitch({ onClick = () => {} }) { + const [icon, setIcon] = useState('arrowDownLine'); -export function SwapSwitch({ - onClick = () => {}, -}) { - const [icon, setIcon] = useState("arrowDownLine") return ( setIcon("arrowLeftRightLine")} - onHoverEnd={() => setIcon("arrowDownLine")} + onHoverStart={() => setIcon('arrowLeftRightLine')} + onHoverEnd={() => setIcon('arrowDownLine')} /> - ) -} \ No newline at end of file + ); +} diff --git a/examples/swap-tokens/components/swap/SwapTokenInput.tsx b/examples/swap-tokens/components/swap/SwapTokenInput.tsx index 6b5aa6170..f957a19d8 100644 --- a/examples/swap-tokens/components/swap/SwapTokenInput.tsx +++ b/examples/swap-tokens/components/swap/SwapTokenInput.tsx @@ -1,40 +1,50 @@ -import { useTheme, Box, Text, Stack, Button, ButtonProps, ChainSwapCombobox, ComboboxOption, useColorModeValue } from '@interchain-ui/react'; +import { + useTheme, + Box, + Text, + Stack, + Button, + ButtonProps, + ChainSwapCombobox, + ComboboxOption, + useColorModeValue, +} from '@interchain-ui/react'; import { Token } from '.'; export function AmountButton({ children, onClick }: ButtonProps) { const attrs = { style: { color: '#fff', - backgroundColor: useColorModeValue('#A2AEBB', '#434B55') - } + backgroundColor: useColorModeValue('#A2AEBB', '#434B55'), + }, }; return ( - ) + ); } export type SwapTokenInputProps = { - token?: Token - tokens?: Token[] - title?: string - amount?: string - $value?: string - disabled?: boolean - balanceLabel?: string - showBalance?: boolean - showMaxButton?: boolean - showHalfButton?: boolean - onAmountChange?: (amount: string) => void - onTokenSelected?: (token: Token) => void - onMaxButtonClick?: () => void - onHalfButtonClick?: () => void -} + token?: Token; + tokens?: Token[]; + title?: string; + amount?: string; + $value?: string; + disabled?: boolean; + balanceLabel?: string; + showBalance?: boolean; + showMaxButton?: boolean; + showHalfButton?: boolean; + onAmountChange?: (amount: string) => void; + onTokenSelected?: (token: Token) => void; + onMaxButtonClick?: () => void; + onHalfButtonClick?: () => void; +}; export function SwapTokenInput({ title = '', - token= {} as Token, + token = {} as Token, tokens = [], amount = '0', $value = '$0', @@ -54,94 +64,126 @@ export function SwapTokenInput({ name: chain?.pretty_name, tokenName: symbol, notionalValue: $value, - })) as ComboboxOption[] + })) as ComboboxOption[]; const { colorMode } = useTheme(); - const current = token?.denom ? { - amount: token.amount, - iconUrl: token.logo, - name: token.chain?.pretty_name, - tokenName: token.symbol, - notionalValue: token.$value, - } as ComboboxOption : options[0] + const current = token?.denom + ? ({ + amount: token.amount, + iconUrl: token.logo, + name: token.chain?.pretty_name, + tokenName: token.symbol, + notionalValue: token.$value, + } as ComboboxOption) + : options[0]; function onItemSelected(item: ComboboxOption) { - onTokenSelected(tokens.find(({ symbol }) => symbol === item.tokenName) as Token) + onTokenSelected( + tokens.find(({ symbol }) => symbol === item.tokenName) as Token + ); } const addon = ( - {disabled - ? {amount} - : onAmountChange(e.target.value)} - style={{ - width: '100%', - fontFamily: 'monospace', - color: colorMode === 'dark' ? 'rgb(238, 242, 248)' : 'rgb(44, 49, 55)', - border: 0, outline: 'none', - appearance: 'none', - fontSize: '22px', - textAlign: 'right', - background: 'transparent' - }} - /> - } - + {disabled ? ( + + {amount} + + ) : ( + onAmountChange(e.target.value)} + style={{ + width: '100%', + fontFamily: 'monospace', + color: + colorMode === 'dark' ? 'rgb(238, 242, 248)' : 'rgb(44, 49, 55)', + border: 0, + outline: 'none', + appearance: 'none', + fontSize: '22px', + textAlign: 'right', + background: 'transparent', + }} + /> + )} + {`≈ ${$value}`} - ) + ); return ( - {title} - - {showBalance ? <> - - {balanceLabel} - - - {token.amount} - - : null} - - {showHalfButton ? Half : null} - {showMaxButton ? Max : null} + + {title} + + + + {showBalance ? ( + <> + + {balanceLabel} + + + {token.amount} + + + ) : null} + + {showHalfButton ? ( + Half + ) : null} + {showMaxButton ? ( + Max + ) : null} - - {options.length > 0 ? - + + {options.length > 0 ? ( + - : null} + + ) : null} - ) -} \ No newline at end of file + ); +} diff --git a/examples/swap-tokens/hooks/useSwap.tsx b/examples/swap-tokens/hooks/useSwap.tsx index c9a5b233c..2b5155ba2 100644 --- a/examples/swap-tokens/hooks/useSwap.tsx +++ b/examples/swap-tokens/hooks/useSwap.tsx @@ -1,27 +1,39 @@ -import BigNumber from "bignumber.js"; +import BigNumber from 'bignumber.js'; import { coin } from '@cosmjs/amino'; -import { Coin } from "@cosmjs/stargate"; -import { FEES, osmosis } from "osmo-query"; -import { useEffect, useState } from "react"; -import { useChain } from "@cosmos-kit/react"; +import { Coin } from '@cosmjs/stargate'; +import { FEES, osmosis } from 'osmo-query'; +import { useEffect, useState } from 'react'; +import { useChain } from '@cosmos-kit/react'; import { Text } from '@interchain-ui/react'; import { toast } from '@interchain-ui/react'; -import { Asset } from "@chain-registry/types"; -import { getExponentByDenom } from "@chain-registry/utils"; -import { CoinDenom, PrettyPair } from "@osmonauts/math/dist/types"; -import { SwapAmountInRoute } from "osmo-query/dist/codegen/osmosis/poolmanager/v1beta1/swap_route"; -import { convertBaseUnitsToDisplayUnits, convertBaseUnitsToDollarValue, PriceHash } from "@chain-registry/utils"; -import { makePoolPairs, getRoutesForTrade, calcPriceImpactGivenIn, calcPriceImpactGivenOut, calcAmountWithSlippage } from "@osmonauts/math"; -import { Token, Slippages, TokenList, SwapInfoProps } from "@/components/swap"; -import { Pools, usePools, usePrices, useBalances, useTx } from "@/hooks"; -import { defaultChainName, Osmosis } from "@/config"; -import { getLogo, integer } from "@/utils"; +import { Asset } from '@chain-registry/types'; +import { getExponentByDenom } from '@chain-registry/utils'; +import { CoinDenom, PrettyPair } from '@osmonauts/math/dist/types'; +import { SwapAmountInRoute } from 'osmo-query/dist/codegen/osmosis/poolmanager/v1beta1/swap_route'; +import { + convertBaseUnitsToDisplayUnits, + convertBaseUnitsToDollarValue, + PriceHash, +} from '@chain-registry/utils'; +import { + makePoolPairs, + getRoutesForTrade, + calcPriceImpactGivenIn, + calcPriceImpactGivenOut, + calcAmountWithSlippage, +} from '@osmonauts/math'; +import { Token, Slippages, TokenList, SwapInfoProps } from '@/components/swap'; +import { Pools, usePools, usePrices, useBalances, useTx } from '@/hooks'; +import { defaultChainName, Osmosis } from '@/config'; +import { getLogo, integer } from '@/utils'; export type Swap = { - to: Token - from: Token - slippage: number -} + to: Token; + from: Token; + slippage: number; +}; + +export type SwapDef = Swap; export function useSwap() { const { tx } = useTx(defaultChainName); @@ -35,7 +47,7 @@ export function useSwap() { // The selected From token. // from.amount is the balance of the From token in display units (atom, osmo ...) - const [amount, setAmount] = useState("0"); + const [amount, setAmount] = useState('0'); // amount is the amount of From token to swap (user input) const [slippage, setSlippage] = useState(Slippages[0]); @@ -45,7 +57,7 @@ export function useSwap() { const prices = usePrices(); const pools = usePools(prices); - const pairs = makePoolPairs(Osmosis.Assets, pools.freefloat.priced, prices); // see pairs data structure at file bottom + const pairs = makePoolPairs(Osmosis.Assets, pools.freefloat.priced, prices); // see pairs data structure at file bottom const denoms = denomsInPairs(pairs); const tokens = newTokens(denoms, prices, balances.hash, from, to); @@ -57,20 +69,26 @@ export function useSwap() { const info = newSwapInfo(swap, pools, routes); const steps = newSwapSteps(swap, pools, routes); - const isLoading = pools.query.isLoading || prices.query.isLoading || balances.query.isLoading; + const isLoading = + pools.query.isLoading || prices.query.isLoading || balances.query.isLoading; const isZeroAmount = new BigNumber(amount).lte(0); const isRoutesEmpty = routes.length === 0; const isInsufficientBalance = new BigNumber(amount).gt(from?.amount || '0'); - const isSwapDisabled = isLoading || isSwapping || isZeroAmount || isRoutesEmpty || isInsufficientBalance; + const isSwapDisabled = + isLoading || + isSwapping || + isZeroAmount || + isRoutesEmpty || + isInsufficientBalance; useEffect(() => { isWalletConnected ? null : setAmount('0'); - }, [isWalletConnected]) + }, [isWalletConnected]); useEffect(() => { if (tokens.length < 2) return; - setFrom((prev) => prev ? tokens.hash[prev.denom] : tokens[0]); - setTo((prev) => prev ? tokens.hash[prev.denom] : tokens[1]); + setFrom((prev) => (prev ? tokens.hash[prev.denom] : tokens[0])); + setTo((prev) => (prev ? tokens.hash[prev.denom] : tokens[1])); }, [pools.query.data, prices.query.data, balances.query.data]); function onFlip() { @@ -87,22 +105,34 @@ export function useSwap() { const num = new BigNumber(amount); const max = from?.amount || '0'; - const val = Number(amount) >= 0 && num.gte(0) ? num.gt(max) ? max : amount : '0'; + const val = + Number(amount) >= 0 && num.gte(0) ? (num.gt(max) ? max : amount) : '0'; setAmount(val); } async function onSwap() { - if (!address || isLoading || isZeroAmount || isInsufficientBalance || isRoutesEmpty) return; + if ( + !address || + isLoading || + isZeroAmount || + isInsufficientBalance || + isRoutesEmpty + ) + return; setIsSwapping(true); const msg = newSwapMessage(swap, address, routes); const fee = FEES.osmosis.swapExactAmountIn(); const res = await tx([msg], { fee }); - setIsSwapping(false) + setIsSwapping(false); if (res.error) { // will be simple as toast.error(res.errorMsg) when interchain-ui fixs the text overflow issue - const error = {res.errorMsg} + const error = ( + + {res.errorMsg} + + ); toast.error(error); console.log(res.response); console.log(res.error.message); @@ -114,12 +144,32 @@ export function useSwap() { } return { - to, setTo, from, setFrom, swap, info, - pools, prices, tokens, balances, routes, steps, - amount, slippage, setSlippage, onFlip, onSwap, onAmountChange, - isLoading, isSwapping, isZeroAmount, isRoutesEmpty, - isSwapDisabled, isWalletConnected, isInsufficientBalance - } + to, + setTo, + from, + setFrom, + swap, + info, + pools, + prices, + tokens, + balances, + routes, + steps, + amount, + slippage, + setSlippage, + onFlip, + onSwap, + onAmountChange, + isLoading, + isSwapping, + isZeroAmount, + isRoutesEmpty, + isSwapDisabled, + isWalletConnected, + isInsufficientBalance, + }; } export function newSwap( @@ -132,20 +182,29 @@ export function newSwap( const swap: Swap = { to: { denom: '', symbol: '', amount: '0', value: '0', $value: '$0' }, from: { denom: '', symbol: '', amount: '0', value: '0', $value: '$0' }, - slippage + slippage, }; if (from && to && prices[from.denom] && prices[to.denom]) { swap.from.denom = from.denom; swap.from.symbol = from.symbol; swap.from.amount = amount; - swap.from.value = new BigNumber(amount).multipliedBy(prices[from.denom]).toString(); - swap.from.$value = `\$${new BigNumber(swap.from.value).decimalPlaces(2).toString()}`; + swap.from.value = new BigNumber(amount) + .multipliedBy(prices[from.denom]) + .toString(); + swap.from.$value = `\$${new BigNumber(swap.from.value) + .decimalPlaces(2) + .toString()}`; swap.to.denom = to.denom; swap.to.symbol = to.symbol; - swap.to.amount = new BigNumber(swap.from.value).div(prices[to.denom]).decimalPlaces(6).toString(); + swap.to.amount = new BigNumber(swap.from.value) + .div(prices[to.denom]) + .decimalPlaces(6) + .toString(); swap.to.value = swap.from.value; - swap.to.$value = `\$${new BigNumber(swap.to.value).decimalPlaces(2).toString()}`; + swap.to.$value = `\$${new BigNumber(swap.to.value) + .decimalPlaces(2) + .toString()}`; } return swap; @@ -158,27 +217,51 @@ export function newTokens( from?: Token, to?: Token ) { - const tokens = denoms.map((denom) => { - const asset = Osmosis.Assets.CoinDenomToAsset[denom]; - const logo = getLogo(asset); - const balance = balances[asset.base]; - const value = balance ? convertBaseUnitsToDollarValue(Osmosis.Assets, prices, asset.symbol, balance.amount) : '0'; - return { - logo, - asset, - denom, - value, - price: prices[asset.base], - chain: Osmosis.getChainByDenom(denom), - symbol: asset.symbol, - amount: balance ? convertBaseUnitsToDisplayUnits(Osmosis.Assets, asset.symbol, balance.amount) : '0', - $value: balance ? `\$${new BigNumber(value).decimalPlaces(2).toString()}` : '$0', - balance: balances[asset.base], - } as Token - }).sort((a, b) => (new BigNumber(a.value!).lt(b.value!) ? 1 : -1)) as TokenList - - tokens.rest = tokens.filter((token) => token.denom !== from?.denom && token.denom !== to?.denom); - tokens.hash = tokens.reduce((acc, token) => ({ ...acc, [token.denom]: token }), {}) as Record; + const tokens = denoms + .map((denom) => { + const asset = Osmosis.Assets.CoinDenomToAsset[denom]; + const logo = getLogo(asset); + const balance = balances[asset.base]; + const value = balance + ? convertBaseUnitsToDollarValue( + Osmosis.Assets, + prices, + asset.symbol, + balance.amount + ) + : '0'; + return { + logo, + asset, + denom, + value, + price: prices[asset.base], + chain: Osmosis.getChainByDenom(denom), + symbol: asset.symbol, + amount: balance + ? convertBaseUnitsToDisplayUnits( + Osmosis.Assets, + asset.symbol, + balance.amount + ) + : '0', + $value: balance + ? `\$${new BigNumber(value).decimalPlaces(2).toString()}` + : '$0', + balance: balances[asset.base], + } as Token; + }) + .sort((a, b) => + new BigNumber(a.value!).lt(b.value!) ? 1 : -1 + ) as TokenList; + + tokens.rest = tokens.filter( + (token) => token.denom !== from?.denom && token.denom !== to?.denom + ); + tokens.hash = tokens.reduce( + (acc, token) => ({ ...acc, [token.denom]: token }), + {} + ) as Record; return tokens; } @@ -187,8 +270,9 @@ export function newCoin(token: Token) { return { denom: token.denom, amount: new BigNumber(token.amount || '0') - .shiftedBy(getExponentByDenom(Osmosis.Assets, token.denom)).toString() - } + .shiftedBy(getExponentByDenom(Osmosis.Assets, token.denom)) + .toString(), + }; } export function newRoutes(swap: Swap, pairs: PrettyPair[]) { @@ -196,12 +280,14 @@ export function newRoutes(swap: Swap, pairs: PrettyPair[]) { const trade = { sell: newCoin(swap.from), buy: newCoin(swap.to), - } + }; return getRoutesForTrade(Osmosis.Assets, { trade, pairs }); } export function denomsInPairs(pairs: PrettyPair[]) { - return Array.from(new Set(pairs.map((pair) => ([pair.baseAddress, pair.quoteAddress])).flat())); + return Array.from( + new Set(pairs.map((pair) => [pair.baseAddress, pair.quoteAddress]).flat()) + ); } export function calcPriceImpact( @@ -209,26 +295,43 @@ export function calcPriceImpact( pools: Pools, routes: SwapAmountInRoute[] = [] ) { - if (new BigNumber(swap.from.amount || '0').isEqualTo(0) || routes.length === 0) return '0'; + if ( + new BigNumber(swap.from.amount || '0').isEqualTo(0) || + routes.length === 0 + ) + return '0'; const to = newCoin(swap.to); const from = newCoin(swap.from); if (routes.length === 1) { - return calcPriceImpactGivenIn(from, to.denom, pools.map.get(routes[0].poolId)!) + return calcPriceImpactGivenIn( + from, + to.denom, + pools.map.get(routes[0].poolId)! + ); } const toRoute = routes.find((route) => route.tokenOutDenom === to.denom)!; const fromRoute = routes.find((route) => route.tokenOutDenom !== to.denom)!; return new BigNumber( - calcPriceImpactGivenIn(from, fromRoute.tokenOutDenom, pools.map.get(fromRoute.poolId)!) - ).plus( - calcPriceImpactGivenOut(to, fromRoute.tokenOutDenom, pools.map.get(toRoute.poolId)!) - ).toString() + calcPriceImpactGivenIn( + from, + fromRoute.tokenOutDenom, + pools.map.get(fromRoute.poolId)! + ) + ) + .plus( + calcPriceImpactGivenOut( + to, + fromRoute.tokenOutDenom, + pools.map.get(toRoute.poolId)! + ) + ) + .toString(); } -export function calcSwapFee(pools: Pools, routes: SwapAmountInRoute[] = [] -) { +export function calcSwapFee(pools: Pools, routes: SwapAmountInRoute[] = []) { if (routes.length === 0) return '0'; return routes.reduce((total, route) => { const pool = pools.map.get(route.poolId)!; @@ -249,63 +352,88 @@ export function newSwapInfo( const priceImpactPercent = priceImpact.decimalPlaces(5).shiftedBy(2); const priceImpactPercentString = priceImpactPercent.toString() + '%'; const swapFee = calcSwapFee(pools, routes); - const swapFeeValue = new BigNumber(swap.from.value || '0').multipliedBy(swapFee).decimalPlaces(2, BigNumber.ROUND_DOWN); - const swapFeePercent = new BigNumber(swapFee).shiftedBy(2) + const swapFeeValue = new BigNumber(swap.from.value || '0') + .multipliedBy(swapFee) + .decimalPlaces(2, BigNumber.ROUND_DOWN); + const swapFeePercent = new BigNumber(swapFee).shiftedBy(2); const swapFeePercentString = swapFeePercent.toString() + '%'; const info = { priceImpact: { number: priceImpact, percent: { number: priceImpactPercent, string: priceImpactPercentString }, - display: priceImpactPercent.gte(0.001) ? priceImpactPercentString : '< 0.001%', + display: priceImpactPercent.gte(0.001) + ? priceImpactPercentString + : '< 0.001%', }, swapFee: { value: swapFeeValue, $value: `\$${swapFeeValue.decimalPlaces(2).toString()}`, number: new BigNumber(swapFee), - percent: { number: swapFeePercent, string: swapFeePercentString } + percent: { number: swapFeePercent, string: swapFeePercentString }, }, expectedOutput: new BigNumber(swap.to.amount!).decimalPlaces(6).toString(), minimumReceived: new BigNumber( convertBaseUnitsToDisplayUnits( - Osmosis.Assets, swap.to.symbol, calcAmountWithSlippage(to.amount, swap.slippage) - )).decimalPlaces(6).toString(), - display: {} as SwapInfoProps - } + Osmosis.Assets, + swap.to.symbol, + calcAmountWithSlippage(to.amount, swap.slippage) + ) + ) + .decimalPlaces(6) + .toString(), + display: {} as SwapInfoProps, + }; // Pad expectedOutput and minimumReceived to the same length - const maxLength = Math.max(info.expectedOutput.length, info.minimumReceived.length); + const maxLength = Math.max( + info.expectedOutput.length, + info.minimumReceived.length + ); info.expectedOutput = info.expectedOutput.padEnd(maxLength, '0'); info.minimumReceived = info.minimumReceived.padEnd(maxLength, '0'); info.display = { priceImpact: info.priceImpact.display, swapFee: { - value: `≈ ${swapFeeValue.gt(0.01) ? info.swapFee.$value : '< \$0.01'}`, - percent: swapFeePercentString + value: `≈ ${swapFeeValue.gt(0.01) ? info.swapFee.$value : '< $0.01'}`, + percent: swapFeePercentString, }, expectedOutput: `≈ ${info.expectedOutput!} ${swap.to.symbol}`, - minimumReceived: `${info.minimumReceived} ${swap.to.symbol}` - } + minimumReceived: `${info.minimumReceived} ${swap.to.symbol}`, + }; return info; } -export function newSwapSteps(swap: Swap, pools: Pools, routes: SwapAmountInRoute[] = []) { +export function newSwapSteps( + swap: Swap, + pools: Pools, + routes: SwapAmountInRoute[] = [] +) { return routes.map((route) => newSwapStep(swap, route, pools)); } -export function newSwapStep(swap: Swap, route: SwapAmountInRoute, pools: Pools) { - const token = (asset: Asset) => - ({ logo: getLogo(asset), denom: asset.base, symbol: asset.symbol }) +export function newSwapStep( + swap: Swap, + route: SwapAmountInRoute, + pools: Pools +) { + const token = (asset: Asset) => ({ + logo: getLogo(asset), + denom: asset.base, + symbol: asset.symbol, + }); const pool = pools.map.get(route.poolId)!; let base, quote; if (route.tokenOutDenom === swap.to.denom) { - base = Osmosis.Assets.CoinDenomToAsset[ - pool.poolAssets.find(({ token }) => token.denom !== swap.to.denom)!.token.denom - ]; + base = + Osmosis.Assets.CoinDenomToAsset[ + pool.poolAssets.find(({ token }) => token.denom !== swap.to.denom)! + .token.denom + ]; quote = Osmosis.Assets.CoinDenomToAsset[swap.to.denom]; } else { base = Osmosis.Assets.CoinDenomToAsset[swap.from.denom]; @@ -313,19 +441,27 @@ export function newSwapStep(swap: Swap, route: SwapAmountInRoute, pools: Pools) } return { poolId: route.poolId.toString(), - swapFee: new BigNumber(pool.poolParams.swapFee).shiftedBy(2).toString() + '%', + swapFee: + new BigNumber(pool.poolParams.swapFee).shiftedBy(2).toString() + '%', base: token(base), quote: token(quote), - } + }; } -function newSwapMessage(swap: Swap, sender: string, routes: SwapAmountInRoute[]) { - const { swapExactAmountIn } = osmosis.gamm.v1beta1.MessageComposer.withTypeUrl; +function newSwapMessage( + swap: Swap, + sender: string, + routes: SwapAmountInRoute[] +) { + const { swapExactAmountIn } = + osmosis.gamm.v1beta1.MessageComposer.withTypeUrl; return swapExactAmountIn({ sender, routes, tokenIn: coin(integer(newCoin(swap.from).amount), swap.from.denom), - tokenOutMinAmount: integer(calcAmountWithSlippage(newCoin(swap.to).amount, swap.slippage)), + tokenOutMinAmount: integer( + calcAmountWithSlippage(newCoin(swap.to).amount, swap.slippage) + ), }); } @@ -641,4 +777,4 @@ function newSwapMessage(swap: Swap, sender: string, routes: SwapAmountInRoute[]) // "quoteSymbol": "NLS", // "quoteAddress": "ibc/D9AFCECDD361D38302AA66EB3BAC23B95234832C51D12489DC451FA2B7C72782" // } -// ] \ No newline at end of file +// ]