diff --git a/index.html b/index.html index 02cfa8c..070b142 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ - Orbs Demo + Orbs Playground
diff --git a/package.json b/package.json index 0f0f878..63f241e 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@ethersproject/hash": "^5.7.0", - "@orbs-network/liquidity-hub-sdk": "^1.0.41", + "@orbs-network/liquidity-hub-sdk": "^1.0.44", "@orbs-network/swap-ui": "^0.0.14", "@orbs-network/twap-sdk": "^2.0.38", "@paraswap/sdk": "^6.10.0", diff --git a/src/App.tsx b/src/App.tsx index 309f0b8..ac22d34 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,13 +1,35 @@ -import { Header } from '@/components/header' -import { Trade } from './trade/trade' +import { Header } from "@/components/header"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "./components/ui/tabs"; +import { SwapLiquidityHub } from "./trade/liquidity-hub/liquidity-hub-swap"; +import { Settings } from "./trade/settings"; +import { SwapTwap, SwapLimit } from "./trade/twap/twap"; export function App() { return ( <>
- +
+

Trade

+ + + + Swap + TWAP + Limit + + + + + + + + + + + +
- ) + ); } diff --git a/src/assets/orbslogo.svg b/src/assets/orbslogo.svg new file mode 100644 index 0000000..45d64d9 --- /dev/null +++ b/src/assets/orbslogo.svg @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/header.tsx b/src/components/header.tsx index d6b42c7..9684d96 100644 --- a/src/components/header.tsx +++ b/src/components/header.tsx @@ -1,12 +1,13 @@ import { ThemeToggle } from '@/components/theme-toggle' import { ConnectButton } from '@rainbow-me/rainbowkit' +import Logo from '@/assets/orbslogo.svg' export function Header() { return (
- - Orbs Demo + + Orbs Playground
diff --git a/src/components/tokens/token-card.tsx b/src/components/tokens/token-card.tsx index c2d4d5b..f1011e6 100644 --- a/src/components/tokens/token-card.tsx +++ b/src/components/tokens/token-card.tsx @@ -3,14 +3,7 @@ import { Card } from "../ui/card"; import { TokenSelect } from "./token-select"; import { Token } from "@/types"; import { NumericFormat } from "react-number-format"; -import { - format, - cn, - ErrorCodes, - useTokensWithBalances, - useTokenBalance, - toExactAmount, -} from "@/lib"; +import { format, cn, ErrorCodes, useTokenBalance, toExactAmount } from "@/lib"; import { Skeleton } from "../ui/skeleton"; import { Button } from "../ui/button"; import { useToExactAmount } from "@/trade/hooks"; @@ -50,7 +43,7 @@ export function TokenCard({ amountLoading, inputError, }: TokenCardProps) { - const balance = useTokenBalance(selectedToken?.address); + const { balance } = useTokenBalance(selectedToken?.address); const balanceError = inputError === ErrorCodes.InsufficientBalance; const balanceDisplay = selectedToken ? format.crypto(Number(toExactAmount(balance, selectedToken.decimals))) diff --git a/src/components/tokens/token-select.tsx b/src/components/tokens/token-select.tsx index fd919e2..110ed72 100644 --- a/src/components/tokens/token-select.tsx +++ b/src/components/tokens/token-select.tsx @@ -13,7 +13,16 @@ import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar"; import { Token } from "@/types"; import { Card } from "../ui/card"; import { useMemo, useState } from "react"; -import { format, toExactAmount, useTokensWithBalances } from "@/lib"; +import { + eqIgnoreCase, + format, + usePriceUsd, + useSortedTokens, + useTokenBalance, +} from "@/lib"; +import { useToExactAmount } from "@/trade/hooks"; +import { Skeleton } from "../ui/skeleton"; +import { Virtuoso } from "react-virtuoso"; import BN from "bignumber.js"; type TokenSelectProps = { @@ -26,49 +35,19 @@ export function TokenSelect({ onSelectToken, }: TokenSelectProps) { const [open, setOpen] = useState(false); - const tokens = useTokensWithBalances().tokensWithBalances + const tokens = useSortedTokens(); const [filterInput, setFilterInput] = useState(""); - const SortedTokens = useMemo(() => { - if(!tokens) return null; - return Object.values(tokens) - .filter((t) => { - return ( - t.token.symbol.toLowerCase().includes(filterInput.toLowerCase()) || - t.token.address.toLowerCase().includes(filterInput.toLowerCase()) - ); - }) - .sort((a, b) => { - - const balanceA = BN(toExactAmount(a.balance.toString(), a.token.decimals)); - const balanceB = BN(toExactAmount(b.balance.toString(), b.token.decimals)); - return balanceB.minus(balanceA).toNumber(); - }) - .map((t) => ( - { - onSelectToken(t.token); - setOpen(false); - }} - > -
- - - - {t.token.symbol.charAt(0)} - - -
-
{t.token.symbol}
-
{t.token.name}
-
-
-
{format.crypto(Number(toExactAmount(t.balance.toString(), t.token.decimals) || '0'))}
-
- )); - }, [filterInput, onSelectToken, tokens]); + const filteredTokens = useMemo(() => { + if (!filterInput) return tokens || []; + return ( + tokens?.filter( + (t) => + eqIgnoreCase(t.address, filterInput) || + t.symbol.toLowerCase().includes(filterInput.toLowerCase()) + ) || [] + ); + }, [tokens, filterInput]); return ( setOpen(o)}> @@ -108,9 +87,64 @@ export function TokenSelect({ />
- {SortedTokens} + { + const token = filteredTokens[index]; + + const onSelect = () => { + onSelectToken(token); + setOpen(false); + }; + + return ; + }} + />
); } + +const TokenDisplay = ({ + token: t, + onSelect, +}: { + token: Token; + onSelect: () => void; +}) => { + const { balance, isLoading } = useTokenBalance(t.address); + const usd = usePriceUsd(t.address).data || 0; + const balanceUi = useToExactAmount(balance, t.decimals) || "0"; + const usdAmount = BN(balanceUi).multipliedBy(usd).toFixed() + + return ( + +
+ + + + {t.symbol.charAt(0)} + + +
+
{t.symbol}
+
{t.name}
+
+
+ {isLoading ? ( + + ) : ( +
+

{format.crypto(Number(balanceUi))}

+

${format.crypto(Number(usdAmount))}

+
+ )} +
+ ); +}; diff --git a/src/lib/index.ts b/src/lib/index.ts index 76d4dcb..1e4a570 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -3,11 +3,11 @@ export * from './networks' export * from './useGetRequiresApproval' export * from './utils' export * from './useHandleInputError' -export * from './useTokensWithBalances' +export * from './useTokens' export * from './useDefaultTokens' export * from './useDebounce' export * from './useParaswap' export * from './wagmi-config' -export * from './useTokenList' +export * from './useTokens' export * from './useWrapOrUnwrapOnly' export * from './usePriceUsd' diff --git a/src/lib/networks.ts b/src/lib/networks.ts index 472acc1..7eb1307 100644 --- a/src/lib/networks.ts +++ b/src/lib/networks.ts @@ -1,54 +1,383 @@ -import { zeroAddress } from 'viem' +import { zeroAddress } from "viem"; export const networks = { + eth: { + id: 1, + name: "Ethereum", + shortname: "eth", + native: { + address: zeroAddress, + symbol: "ETH", + decimals: 18, + logoUrl: "https://app.1inch.io/assets/images/network-logos/ethereum.svg", + }, + wToken: { + symbol: "WETH", + address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + decimals: 18, + weth: true, + logoUrl: + "https://tokens-data.1inch.io/images/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.png", + }, + publicRpcUrl: "https://eth.llamarpc.com", + logoUrl: "https://app.1inch.io/assets/images/network-logos/ethereum.svg", + explorer: "https://etherscan.io", + eip1559: true, + baseAssets: [ + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", + "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "0xdAC17F958D2ee523a2206206994597C13D831ec7", + "0x6B175474E89094C44Da98b954EedeAC495271d0F", + "0x5f98805A4E8be255a32880FDeC7F6728C6568bA0", + "0x853d955aCEf822Db058eb8505911ED77F175b99e", + "0xff56Cc6b1E6dEd347aA0B7676C85AB0B3D08B0FA", + ], + }, + bsc: { + id: 56, + name: "BinanceSmartChain", + shortname: "bsc", + native: { + address: zeroAddress, + symbol: "BNB", + decimals: 18, + logoUrl: "https://app.1inch.io/assets/images/network-logos/bsc_2.svg", + }, + wToken: { + symbol: "WBNB", + address: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", + decimals: 18, + weth: true, + logoUrl: + "https://tokens-data.1inch.io/images/0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c.png", + }, + publicRpcUrl: "https://bsc-dataseed.binance.org", + logoUrl: "https://app.1inch.io/assets/images/network-logos/bsc_2.svg", + explorer: "https://bscscan.com", + eip1559: false, + baseAssets: [ + "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", + "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c", + "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", + "0x55d398326f99059fF775485246999027B3197955", + "0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3", + "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56", + "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", + "0xeBd49b26169e1b52c04cFd19FCf289405dF55F80", + ], + }, poly: { id: 137, - name: 'Polygon', - shortname: 'poly', + name: "Polygon", + shortname: "poly", native: { address: zeroAddress, - symbol: 'MATIC', + symbol: "MATIC", decimals: 18, - logoUrl: 'https://app.1inch.io/assets/images/network-logos/polygon.svg', + logoUrl: "https://app.1inch.io/assets/images/network-logos/polygon.svg", }, wToken: { - symbol: 'WMATIC', - address: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', + symbol: "WMATIC", + address: "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", decimals: 18, weth: true, logoUrl: - 'https://tokens-data.1inch.io/images/0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270.png', + "https://tokens-data.1inch.io/images/0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270.png", }, - publicRpcUrl: 'https://polygon-rpc.com', - logoUrl: 'https://app.1inch.io/assets/images/network-logos/polygon.svg', - explorer: 'https://polygonscan.com', + publicRpcUrl: "https://polygon-rpc.com", + logoUrl: "https://app.1inch.io/assets/images/network-logos/polygon.svg", + explorer: "https://polygonscan.com", eip1559: true, + baseAssets: [ + "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", + "0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4", + "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6", + "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", + "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", + "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063", + "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619", + "0xdAb529f40E671A1D4bF91361c21bf9f0C9712ab7", + "0x614389EaAE0A6821DC49062D56BDA3d9d45Fa2ff", + ], + }, + arb: { + id: 42161, + name: "Arbitrum", + shortname: "arb", + native: { + address: zeroAddress, + symbol: "ETH", + decimals: 18, + logoUrl: "https://app.1inch.io/assets/images/network-logos/ethereum.svg", + }, + wToken: { + symbol: "WETH", + address: "0x82af49447d8a07e3bd95bd0d56f35241523fbab1", + decimals: 18, + weth: true, + logoUrl: + "https://tokens-data.1inch.io/images/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.png", + }, + publicRpcUrl: "https://arb1.arbitrum.io/rpc", + logoUrl: "https://app.1inch.io/assets/images/network-logos/arbitrum.svg", + explorer: "https://arbiscan.io", + eip1559: true, + baseAssets: [ + "0x82af49447d8a07e3bd95bd0d56f35241523fbab1", + "0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f", + "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8", + "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", + "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1", + "0x912CE59144191C1204E64559FE8253a0e49E6548", + "0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F", + "0x4D15a3A2286D883AF0AA1B3f21367843FAc63E07", + ], + }, + avax: { + id: 43114, + name: "Avalanche", + shortname: "avax", + native: { + address: zeroAddress, + symbol: "AVAX", + decimals: 18, + logoUrl: "https://app.1inch.io/assets/images/network-logos/avalanche.svg", + }, + wToken: { + symbol: "WAVAX", + address: "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", + decimals: 18, + weth: true, + logoUrl: + "https://tokens-data.1inch.io/images/0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7.png", + }, + publicRpcUrl: "https://api.avax.network/ext/bc/C/rpc", + logoUrl: "https://app.1inch.io/assets/images/network-logos/avalanche.svg", + explorer: "https://snowtrace.io", + eip1559: true, + baseAssets: [ + "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", + "0x50b7545627a5162F82A992c33b87aDc75187B218", + "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", + "0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664", + "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7", + "0xc7198437980c041c805A1EDcbA50c1Ce5db95118", + "0x19860CCB0A68fd4213aB9D8266F7bBf05A8dDe98", + "0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB", + "0xd586E7F844cEa2F87f50152665BCbc2C279D8d70", + "0x340fE1D898ECCAad394e2ba0fC1F93d27c7b717A", + ], + }, + oeth: { + id: 10, + name: "Optimism", + shortname: "oeth", + native: { + address: zeroAddress, + symbol: "ETH", + decimals: 18, + logoUrl: "https://app.1inch.io/assets/images/network-logos/ethereum.svg", + }, + wToken: { + symbol: "WETH", + address: "0x4200000000000000000000000000000000000006", + decimals: 18, + weth: true, + logoUrl: + "https://tokens-data.1inch.io/images/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.png", + }, + publicRpcUrl: "https://mainnet.optimism.io", + logoUrl: "https://app.1inch.io/assets/images/network-logos/optimism.svg", + explorer: "https://optimistic.etherscan.io", + eip1559: true, + baseAssets: [ + "0x4200000000000000000000000000000000000006", + "0x68f180fcCe6836688e9084f035309E29Bf0A2095", + "0x7F5c764cBc14f9669B88837ca1490cCa17c31607", + "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58", + "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1", + "0x4200000000000000000000000000000000000042", + "0x2E3D870790dC77A83DD1d18184Acc7439A53f475", + ], + }, + ftm: { + id: 250, + name: "Fantom", + shortname: "ftm", + native: { + address: zeroAddress, + symbol: "FTM", + decimals: 18, + logoUrl: "https://app.1inch.io/assets/images/network-logos/fantom.svg", + }, + wToken: { + symbol: "WFTM", + address: "0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83", + decimals: 18, + weth: true, + logoUrl: + "https://tokens-data.1inch.io/images/0x4e15361fd6b4bb609fa63c81a2be19d873717870.png", + }, + publicRpcUrl: "https://rpc.ftm.tools", + logoUrl: "https://app.1inch.io/assets/images/network-logos/fantom.svg", + explorer: "https://ftmscan.com", + eip1559: true, + baseAssets: [ + "0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83", + "0x321162Cd933E2Be498Cd2267a90534A804051b11", + "0x74b23882a30290451A17c44f4F05243b6b58C76d", + "0x04068DA6C83AFCFA0e13ba15A6696662335D5B75", + "0x8D11eC38a3EB5E956B052f67Da8Bdc9bef8Abf3E", + "0xdc301622e621166BD8E82f2cA0A26c13Ad0BE355", + "0x3E01B7E242D5AF8064cB9A8F9468aC0f8683617c", + ], + }, + base: { + id: 8453, + name: "Base", + shortname: "base", + native: { + address: zeroAddress, + symbol: "ETH", + decimals: 18, + logoUrl: "https://app.1inch.io/assets/images/network-logos/ethereum.svg", + }, + wToken: { + symbol: "WETH", + address: "0x4200000000000000000000000000000000000006", + decimals: 18, + weth: true, + logoUrl: + "https://tokens-data.1inch.io/images/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.png", + }, + publicRpcUrl: "https://mainnet.base.org", + logoUrl: "https://app.1inch.io/assets/images/network-logos/base.svg", + explorer: "https://basescan.org", + eip1559: false, + baseAssets: [ + "0x4200000000000000000000000000000000000006", + "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb", + ], + }, + linea: { + id: 59144, + name: "Linea", + shortname: "linea", + native: { + address: zeroAddress, + symbol: "ETH", + decimals: 18, + logoUrl: "https://app.1inch.io/assets/images/network-logos/ethereum.svg", + }, + wToken: { + symbol: "WETH", + address: "0xe5D7C2a44FfDDf6b295A15c148167daaAf5Cf34f", + decimals: 18, + weth: true, + logoUrl: + "https://tokens-data.1inch.io/images/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.png", + }, + publicRpcUrl: "https://rpc.linea.build", + logoUrl: "https://lineascan.build/images/logo.svg", + explorer: "https://lineascan.build", + eip1559: false, + baseAssets: [ + "0xe5D7C2a44FfDDf6b295A15c148167daaAf5Cf34f", + "0x176211869cA2b568f2A7D4EE941E073a821EE1ff", + "0x4AF15ec2A0BD43Db75dd04E62FAA3B8EF36b00d5", + ], + }, + zksync: { + id: 324, + name: "zksync", + shortname: "zksync", + native: { + address: zeroAddress, + symbol: "ETH", + decimals: 18, + logoUrl: "https://app.1inch.io/assets/images/network-logos/ethereum.svg", + }, + wToken: { + symbol: "WETH", + address: "0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91", + decimals: 18, + weth: true, + logoUrl: + "https://tokens-data.1inch.io/images/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.png", + }, + publicRpcUrl: "https://mainnet.era.zksync.io", + logoUrl: + "https://raw.githubusercontent.com/matter-labs/zksync/0a4ca2145a0c95b5bafa84c2f095c644907a8825/zkSyncLogo.svg", + explorer: "https://explorer.zksync.io/", + eip1559: true, + baseAssets: [ + "0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91", + "0x3355df6d4c9c3035724fd0e3914de96a5a83aaf4", + "0x4b9eb6c0b6ea15176bbf62841c6b2a8a398cb656", + ], + }, + zkevm: { + id: 1101, + name: "zkevm", + shortname: "zkevm", + native: { + address: zeroAddress, + symbol: "ETH", + decimals: 18, + logoUrl: "https://app.1inch.io/assets/images/network-logos/ethereum.svg", + }, + wToken: { + symbol: "WETH", + address: "0x4F9A0e7FD2Bf6067db6994CF12E4495Df938E6e9", + decimals: 18, + weth: true, + logoUrl: + "https://tokens-data.1inch.io/images/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.png", + }, + publicRpcUrl: "https://zkevm-rpc.com", + logoUrl: + "https://user-images.githubusercontent.com/18598517/235932702-bc47eae5-d672-4dd9-9da2-8ea8f51a93f3.png", + explorer: "https://zkevm.polygonscan.com/", + eip1559: true, + baseAssets: [ + "0x4F9A0e7FD2Bf6067db6994CF12E4495Df938E6e9", + "0xA8CE8aee21bC2A48a5EF670afCc9274C7bbbC035", + "0xC5015b9d9161Dca7e18e32f6f25C4aD850731Fd4", + ], }, -} - -export const network = { - 137: { - id: 137, - name: 'Polygon', - shortname: 'poly', + blast: { + id: 81457, + name: "blast", + shortname: "blast", native: { address: zeroAddress, - symbol: 'MATIC', + symbol: "ETH", decimals: 18, - logoUrl: 'https://app.1inch.io/assets/images/network-logos/polygon.svg', + logoUrl: "https://icons.llamao.fi/icons/chains/rsz_blast", }, wToken: { - symbol: 'WMATIC', - address: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', + symbol: "WETH", + address: "0x4300000000000000000000000000000000000004", decimals: 18, weth: true, logoUrl: - 'https://tokens-data.1inch.io/images/0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270.png', + "https://tokens-data.1inch.io/images/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.png", }, - publicRpcUrl: 'https://polygon-rpc.com', - logoUrl: 'https://app.1inch.io/assets/images/network-logos/polygon.svg', - explorer: 'https://polygonscan.com', + publicRpcUrl: "https://rpc.ankr.com/blast", + logoUrl: "https://icons.llamao.fi/icons/chains/rsz_blast", + explorer: "https://blastscan.io/", eip1559: true, + baseAssets: [ + "0x4300000000000000000000000000000000000004", + "0x4300000000000000000000000000000000000003", + ], }, -} \ No newline at end of file +}; + +export const getNetwork = (chainId: number) => { + return Object.values(networks).find((network) => network.id === chainId); +}; + diff --git a/src/lib/useBalances.ts b/src/lib/useBalances.ts deleted file mode 100644 index 15f870f..0000000 --- a/src/lib/useBalances.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import { getBalance, multicall } from '@wagmi/core' -import { Config, serialize, useBalance, useConfig } from 'wagmi' -import { GetBalanceReturnType } from 'wagmi/actions' -import { Token, TokensWithBalances } from '@/types' -import { erc20Abi, isAddress, Address } from 'viem' -import { zeroAddress } from '@orbs-network/liquidity-hub-sdk' - -interface QueryBalanceParams { - chainId: number | undefined - tokens: Token[] - account: string | undefined - nativeBalance?: GetBalanceReturnType - config: Config -} - -export const queryFnUseBalances = async ({ - chainId, - tokens, - account, - nativeBalance, - config, -}: QueryBalanceParams) => { - if (!account || !chainId || !tokens) return null - - let native = nativeBalance - if (typeof native === 'undefined') { - native = await getBalance(config, { - address: account as Address, - chainId, - }) - } - - const [validatedTokens, validatedTokenAddresses] = tokens.reduce< - [Token[], Address[]] - >( - (acc, tokens) => { - if (chainId && tokens && isAddress(tokens.address)) { - acc[0].push(tokens) - acc[1].push(tokens.address as Address) - } - - return acc - }, - [[], []] - ) - - const data = await multicall(config, { - contracts: validatedTokenAddresses.map( - (token) => - ({ - chainId, - address: token, - abi: erc20Abi, - functionName: 'balanceOf', - args: [account], - } as const) - ), - }) - - const _data = data.reduce((acc, _cur, i) => { - const amount = data[i].result - if (typeof amount === 'bigint') { - acc[validatedTokens[i].address] = { - token: validatedTokens[i], - balance: amount, - } - } - return acc - }, {}) - - _data[zeroAddress] = { - token: validatedTokens[0], - balance: native.value, - } - - return _data -} - -interface UseBalanceParams { - chainId: number | undefined - tokens: Token[] - account: Address | undefined - enabled?: boolean -} - -export const useBalances = ({ - chainId, - tokens, - account, - enabled = true, -}: UseBalanceParams) => { - const { data: nativeBalance, queryKey } = useBalance({ - chainId, - address: account, - query: { enabled, refetchInterval: 10000, staleTime: 10000 }, - }) - - const config = useConfig() - - return { - query: useQuery({ - queryKey: [ - 'useBalances', - { chainId, tokens, account, nativeBalance: serialize(nativeBalance) }, - ], - queryFn: () => - queryFnUseBalances({ - chainId, - tokens, - account, - nativeBalance, - config, - }), - refetchInterval: 10000, - staleTime: 10000, - enabled: Boolean(chainId && account && enabled && tokens), - }), - queryKey: [...queryKey, 'useBalances'], - } -} diff --git a/src/lib/useDefaultTokens.ts b/src/lib/useDefaultTokens.ts index 96b5024..fa44c93 100644 --- a/src/lib/useDefaultTokens.ts +++ b/src/lib/useDefaultTokens.ts @@ -1,48 +1,14 @@ -import { Token, TokensWithBalances } from '@/types' -import { zeroAddress } from '@orbs-network/liquidity-hub-sdk' -import { useEffect, useMemo } from 'react' - -/* Sets default tokens */ -type UseDefaultTokens = { - inToken: Token | null - outToken: Token | null - tokensWithBalances: TokensWithBalances | null | undefined - setInToken: (token: Token) => void - setOutToken: (token: Token) => void -} -export function useDefaultTokens({ - tokensWithBalances, - inToken, - outToken, - setInToken, - setOutToken, -}: UseDefaultTokens) { - const defaultTokens = useMemo(() => { - if (!tokensWithBalances) return [] - - return [ - tokensWithBalances[zeroAddress].token, - Object.values(tokensWithBalances).find((t) => t.token.symbol === 'USDT') - ?.token || null, - ].filter(Boolean) as Token[] - }, [tokensWithBalances]) - - useEffect(() => { - if (!inToken && tokensWithBalances) { - setInToken(defaultTokens[0]) - } - - if (!outToken && tokensWithBalances) { - setOutToken(defaultTokens[1]) - } - }, [ - inToken, - defaultTokens, - outToken, - setInToken, - setOutToken, - tokensWithBalances, - ]) - - return defaultTokens +import { useMemo } from "react"; +import { useSortedTokens } from "./useTokens"; + +export function useDefaultTokens() { + const tokens = useSortedTokens(); + + return useMemo(() => { + if (!tokens) return; + return { + inToken: tokens[0], + outToken: tokens[1], + }; + }, [tokens]); } diff --git a/src/lib/useGetRequiresApproval.ts b/src/lib/useGetRequiresApproval.ts index 8d0988b..788ced4 100644 --- a/src/lib/useGetRequiresApproval.ts +++ b/src/lib/useGetRequiresApproval.ts @@ -3,7 +3,7 @@ import { Address, erc20Abi } from 'viem' /* Determines whether user needs tp approve allowance for quoted token */ export function useGetRequiresApproval( - contractAddress?: Address, + contractAddress?: any, inTokenAddress = '', inAmount = '' ) { diff --git a/src/lib/useHandleInputError.ts b/src/lib/useHandleInputError.ts index 2ee712a..750341d 100644 --- a/src/lib/useHandleInputError.ts +++ b/src/lib/useHandleInputError.ts @@ -1,10 +1,10 @@ -import { ErrorCodes, toExactAmount } from "@/lib/utils"; +import { ErrorCodes } from "@/lib/utils"; import { Token } from "@/types"; import { useMemo } from "react"; -import { - useTokenBalance, -} from "./useTokensWithBalances"; import BN from "bignumber.js"; +import { useTokenBalance } from "./useTokens"; +import { useToRawAmount } from "@/trade/hooks"; + /* Handles amount input errors */ @@ -15,15 +15,18 @@ export function useInputError({ inToken: Token | null; inputAmount: string; }) { - const tokenBalance = useTokenBalance(inToken?.address); + const {balance} = useTokenBalance(inToken?.address); + const parsedInputAmount = useToRawAmount(inputAmount, inToken?.decimals) return useMemo(() => { - if (!inputAmount) { + if(BN(inputAmount || '0').lte(0)) { + return ErrorCodes.EnterAmount; + } + if (!balance) { return ErrorCodes.EnterAmount; } - const balance = toExactAmount(tokenBalance, inToken?.decimals); - if (BN(inputAmount).gt(balance)) { + if (BN(parsedInputAmount).gt(balance)) { return ErrorCodes.InsufficientBalance; } - }, [inputAmount, inToken, tokenBalance]); + }, [inputAmount, inToken, balance, parsedInputAmount]); } diff --git a/src/lib/useParaswap.ts b/src/lib/useParaswap.ts index 47eb488..cd716ea 100644 --- a/src/lib/useParaswap.ts +++ b/src/lib/useParaswap.ts @@ -2,19 +2,11 @@ import { useCallback, useMemo } from 'react' import { getMinAmountOut, isNativeAddress, - resolveNativeTokenAddress, - waitForConfirmations, } from '@/lib/utils' import { constructSimpleSDK, OptimalRate, SwapSide } from '@paraswap/sdk' -import { useMutation, useQuery } from '@tanstack/react-query' +import { useQuery } from '@tanstack/react-query' import { useAccount } from 'wagmi' -import { wagmiConfig } from './wagmi-config' -import { estimateGas, sendTransaction } from 'wagmi/actions' -import { Address } from 'viem' -import { SwapStatus } from '@orbs-network/swap-ui' -import { SwapSteps } from '@/types' -import { approveAllowance } from './approveAllowance' -import { getRequiresApproval } from './getRequiresApproval' + const PARASWAP_NATIVE_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' diff --git a/src/lib/usePriceUsd.ts b/src/lib/usePriceUsd.ts index e81a13d..c273a39 100644 --- a/src/lib/usePriceUsd.ts +++ b/src/lib/usePriceUsd.ts @@ -1,4 +1,4 @@ -import { networks, isNativeAddress } from '@/lib' +import { isNativeAddress, getNetwork } from '@/lib' import { useQuery } from '@tanstack/react-query' import { useAccount } from 'wagmi' @@ -6,14 +6,14 @@ export const usePriceUsd = (address?: string) => { const {chainId} = useAccount() return useQuery({ queryKey: ['usePriceUSD', chainId, address], - queryFn: async () => { + queryFn: async () => { if (!address || !chainId) { return 0 } return (await fetchLLMAPrice(address, chainId)).priceUsd }, - refetchInterval: 10_000, + refetchInterval: 30_000, enabled: !!address && !!chainId, }) } @@ -40,7 +40,7 @@ export async function fetchLLMAPrice(token: string, chainId: number) { const chainName = chainIdToName[chainId] || 'Unknown Chain' if (isNativeAddress(token)) { - token = networks.poly.wToken.address + token = getNetwork(chainId)?.wToken.address || '' } const tokenAddressWithChainId = `${chainName}:${token}` const url = `https://coins.llama.fi/prices/current/${tokenAddressWithChainId}` @@ -49,10 +49,10 @@ export async function fetchLLMAPrice(token: string, chainId: number) { return nullPrice } const data = await response.json() - const coin = data.coins[tokenAddressWithChainId] + const coin = data.coins[tokenAddressWithChainId] return { - priceUsd: coin.price, - priceNative: coin.price, + priceUsd: !coin ? 0 : coin.price, + priceNative: !coin ? 0 : coin.price, timestamp: Date.now(), } } catch (error) { diff --git a/src/lib/useTokenList.ts b/src/lib/useTokenList.ts deleted file mode 100644 index f887dcd..0000000 --- a/src/lib/useTokenList.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { networks } from '@/lib/networks' -import { Token } from '@/types' -import { zeroAddress } from '@orbs-network/liquidity-hub-sdk' -import { useQuery } from '@tanstack/react-query' - -type PolygonToken = { - address: string - chainId: number - decimals: number - logoURI: string - name?: string - symbol: string -} - -const getPolygonTokens = async (): Promise => { - const res = await fetch( - 'https://unpkg.com/quickswap-default-token-list@1.3.16/build/quickswap-default.tokenlist.json' - ) - - if (!res.ok) { - throw new Error('Failed to fetch tokens') - } - - const polyTokens = (await res.json()).tokens as PolygonToken[] - - const tokens = polyTokens.filter((it) => it.chainId === networks.poly.id) - - const candiesAddresses = [ - zeroAddress, - '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', - '0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4', - '0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6', - '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', - '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', - '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', - '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619', - '0xdAb529f40E671A1D4bF91361c21bf9f0C9712ab7', - '0x614389EaAE0A6821DC49062D56BDA3d9d45Fa2ff', - ] - - const sorted = tokens.sort((a, b) => { - const indexA = candiesAddresses.indexOf(a.address) - const indexB = candiesAddresses.indexOf(b.address) - return indexB - indexA - }) - - return [ - { - address: zeroAddress, - symbol: 'MATIC', - decimals: 18, - logoURI: 'https://app.1inch.io/assets/images/network-logos/polygon.svg', - name: 'MATIC', - }, - ...sorted, - ].map((token) => { - return { - address: token.address, - symbol: token.symbol, - decimals: token.decimals, - logoUrl: token.logoURI?.replace('/logo_24.png', '/logo_48.png'), - name: token.name, - } - }) -} - - - -export function useTokensList() { - const chainId = networks.poly.id - - return useQuery({ - queryFn: async () => { - return getPolygonTokens() || [] - }, - queryKey: ['tokens-list', chainId], - staleTime: Infinity, - }) -} diff --git a/src/lib/useTokens.ts b/src/lib/useTokens.ts new file mode 100644 index 0000000..ebf63e7 --- /dev/null +++ b/src/lib/useTokens.ts @@ -0,0 +1,205 @@ +import { Token } from "@/types"; +import { getNetwork, networks } from "./networks"; +import { useQuery } from "@tanstack/react-query"; +import { getBalance, multicall } from "@wagmi/core"; +import { useAccount, useConfig } from "wagmi"; +import { erc20Abi, Address } from "viem"; +import { zeroAddress } from "@orbs-network/liquidity-hub-sdk"; +import { eqIgnoreCase } from "./utils"; +import { useMemo } from "react"; + +const getFantomTokens = async (signal?: AbortSignal): Promise => { + const res = await fetch( + "https://raw.githubusercontent.com/viaprotocol/tokenlists/main/tokenlists/ftm.json", + { signal } + ); + const data = await res.json(); + return data.map((token: any) => { + return { + address: token.address, + symbol: token.symbol, + decimals: token.decimals, + logoUrl: token.logoURI, + name: token.name, + }; + }); +}; + +const getSushiTokens = async ( + chainId: number, + signal?: AbortSignal +): Promise => { + const tokens = await fetch("https://token-list.sushi.com/", { signal }).then( + (res) => + res + .json() + .then((it) => it.tokens.filter((it: any) => it.chainId === chainId)) + ); + + return Object.values(tokens).map((token: any) => { + return { + address: token.address, + symbol: token.symbol, + decimals: token.decimals, + logoUrl: token.logoURI, + name: token.name, + }; + }); +}; + +const getLineaTokens = async (signal?: AbortSignal): Promise => { + const tokens = await fetch("https://api.lynex.fi/api/v1/assets", { signal }) + .then((res) => res.json()) + .then((res) => res.data); + return tokens.map((token: any) => { + return { + address: token.address, + symbol: token.symbol, + decimals: token.decimals, + logoUrl: token.logoURI, + name: token.name, + }; + }); +}; + +const fetchTokens = async ( + chainId: number, + signal?: AbortSignal +): Promise => { + let tokens: Token[] = []; + if (chainId === networks.linea.id) { + tokens = await getLineaTokens(signal); + } else if (chainId === networks.ftm.id) { + tokens = await getFantomTokens(signal); + } else { + tokens = await getSushiTokens(chainId, signal); + } + const network = getNetwork(chainId); + if (network) { + const nativeToken: Token = { + address: network.native.address, + symbol: network.native.symbol, + decimals: network.native.decimals, + logoUrl: network.native.logoUrl, + }; + + tokens = [nativeToken, ...tokens]; + } + + const baseAssets = getNetwork(chainId)?.baseAssets; + if (!baseAssets) { + return tokens; + } + const sortedTokens = tokens.sort((a, b) => { + const aPriority = baseAssets.includes(a.address) ? 0 : 1; + const bPriority = baseAssets.includes(b.address) ? 0 : 1; + if (aPriority !== bPriority) { + return aPriority - bPriority; + } + return a.address.localeCompare(b.address); + }); + + return sortedTokens; +}; + +const useTokensList = () => { + const chainId = useAccount().chainId; + + return useQuery({ + queryFn: async ({ signal }) => { + const response = await fetchTokens(chainId!, signal); + return response; + }, + queryKey: ["useTokensList", chainId], + staleTime: Infinity, + enabled: !!chainId, + }); +}; + +type BalancesReponse = Record; + +export const useTokenBalaces = () => { + const { data: tokens } = useTokensList(); + const { address: account, chainId } = useAccount(); + + const config = useConfig(); + + return useQuery({ + queryKey: ["useBalances", chainId, account, tokens?.map((t) => t.address)], + queryFn: async () => { + if (!tokens) return {}; + let native = await getBalance(config, { + address: account as Address, + chainId, + }); + + const addresses = tokens + .map((token) => token.address) + .filter((it) => !eqIgnoreCase(it, zeroAddress)); + + const multicallResponse = await (multicall as any)(config, { + contracts: addresses.map( + (address) => + ({ + chainId, + address, + abi: erc20Abi, + functionName: "balanceOf", + args: [account], + } as const) + ), + }); + + + const balances = addresses.reduce( + (acc: any, address: any, index: number) => { + acc[address] = multicallResponse[index].result?.toString() || '0'; + return acc; + }, + {} + ); + + balances[zeroAddress] = native.value.toString(); + + return balances; + }, + refetchInterval: 20_000, + staleTime: Infinity, + enabled: Boolean(chainId && account && tokens?.length), + }); +}; + +export const useTokenBalance = (tokenAddress?: string) => { + const { data: balances, isLoading } = useTokenBalaces(); + return useMemo(() => { + if (!tokenAddress) { + return { + isLoading, + balance: "0", + }; + } + return { + isLoading, + balance: balances?.[tokenAddress] || "0", + }; + }, [balances, tokenAddress, isLoading]); +}; + +export const useSortedTokens = () => { + const { data: tokens } = useTokensList(); + const { data: balances } = useTokenBalaces(); + return useMemo(() => { + const sorted = tokens?.sort((a, b) => { + const balanceA = BigInt(balances?.[a.address] || "0"); + const balanceB = BigInt(balances?.[b.address] || "0"); + return balanceB > balanceA ? 1 : balanceB < balanceA ? -1 : 0; + }); + + const native = sorted?.find((it) => eqIgnoreCase(it.address, zeroAddress)); + if (native) { + sorted?.splice(sorted.indexOf(native), 1); + sorted?.unshift(native); + } + return sorted; + }, [tokens, balances]); +}; diff --git a/src/lib/useTokensWithBalances.ts b/src/lib/useTokensWithBalances.ts deleted file mode 100644 index c1d2bdc..0000000 --- a/src/lib/useTokensWithBalances.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { useAccount } from "wagmi"; -import { useTokensList } from "./useTokenList"; -import { useBalances } from "./useBalances"; -import { TokensWithBalances } from "@/types"; - -export function useTokensWithBalances() { - const { address, chainId } = useAccount(); - const { data: tokens, isLoading: tokensLoading } = useTokensList(); - const { - query: { data: balances, isLoading: balancesLoading, refetch }, - queryKey, - } = useBalances({ - chainId, - tokens: tokens || [], - account: address, - enabled: Boolean(tokens && address && chainId), - }); - - return { - isLoading: tokensLoading || balancesLoading, - tokensWithBalances: balances as TokensWithBalances, - queryKey, - refetch, - }; -} - -export const useTokenBalance = (tokenAddress?: string) => { - const { tokensWithBalances } = useTokensWithBalances(); - return !tokenAddress - ? "" - : tokensWithBalances?.[tokenAddress]?.balance.toString(); -}; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 58f6527..d832aae 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -21,7 +21,7 @@ export const toExactAmount = ( if (decimalScale) { return result.toFixed(decimalScale); } - return result.toString(); + return result.toFixed(); }; export const toRawAmount = (amount?: string, decimals?: number) => { if (!decimals || !amount) return ""; @@ -72,7 +72,6 @@ export const format = { }; export const getMinAmountOut = (slippage: number, _destAmount: string) => { - console.log(_destAmount); const slippageFactor = BigInt(1000 - Math.floor(slippage * 10)); // 0.5% becomes 995 diff --git a/src/lib/wagmi-config.ts b/src/lib/wagmi-config.ts index 68cb342..2db44ed 100644 --- a/src/lib/wagmi-config.ts +++ b/src/lib/wagmi-config.ts @@ -1,10 +1,14 @@ import { getDefaultConfig } from '@rainbow-me/rainbowkit' -import { polygon } from 'viem/chains' +import { http } from 'viem' +import { polygon, mainnet, arbitrum, bsc, fantom, blast, linea } from 'viem/chains' const walletConnectProjectId = import.meta.env.VITE_WALLET_CONNECT_PROJECT_ID export const wagmiConfig = getDefaultConfig({ appName: 'DEX Playground', projectId: walletConnectProjectId, - chains: [polygon], + chains: [polygon, mainnet, arbitrum, bsc, fantom, blast, linea], + transports: { + [mainnet.id]: http(`https://rpcman.orbs.network/rpc?chainId=1&appId=dex-playground`) + } }) diff --git a/src/store.ts b/src/store.ts new file mode 100644 index 0000000..a663c4b --- /dev/null +++ b/src/store.ts @@ -0,0 +1,18 @@ +import { create } from "zustand"; +import { persist } from "zustand/middleware"; + +interface AppStore { + slippage: number; + setSlippage: (slippage: number) => void; +} +export const useAppState = create( + persist( + (set) => ({ + slippage: 0.5, + setSlippage: (slippage: number) => set({ slippage }), + }), + { + name: "main-store", + } + ) +); diff --git a/src/tokens.json b/src/tokens.json new file mode 100644 index 0000000..e69de29 diff --git a/src/trade/hooks.ts b/src/trade/hooks.ts index 9681e40..9cb69f1 100644 --- a/src/trade/hooks.ts +++ b/src/trade/hooks.ts @@ -1,4 +1,4 @@ -import { network, toExactAmount, toRawAmount } from "@/lib"; +import { getNetwork, toExactAmount, toRawAmount } from "@/lib"; import { useMemo } from "react"; import { useChainId } from "wagmi"; @@ -15,7 +15,7 @@ export const useNetwork = () => { return useMemo(() => { if (!chainId) return; - return network[chainId as keyof typeof network]; + return getNetwork(chainId); }, [chainId]); }; diff --git a/src/trade/liquidity-hub/context.tsx b/src/trade/liquidity-hub/context.tsx index 948b1ab..0780cee 100644 --- a/src/trade/liquidity-hub/context.tsx +++ b/src/trade/liquidity-hub/context.tsx @@ -1,107 +1,119 @@ -import { useDefaultTokens, useTokensWithBalances } from "@/lib"; +import { useDefaultTokens } from "@/lib"; import { Token } from "@/types"; -import { constructSDK, LiquidityHubSDK, Quote } from "@orbs-network/liquidity-hub-sdk"; -import { useContext, ReactNode, useReducer, useCallback, useMemo, createContext } from "react"; +import { + constructSDK, + LiquidityHubSDK, + Quote, +} from "@orbs-network/liquidity-hub-sdk"; +import { + useContext, + ReactNode, + useReducer, + useCallback, + useMemo, + createContext, +} from "react"; import { useAccount } from "wagmi"; import { useToRawAmount } from "../hooks"; const initialState: State = { - inToken: null, - outToken: null, - inputAmount: "", - acceptedQuote: undefined, - liquidityHubDisabled: false, - slippage: 0.5, - forceLiquidityHub: false, - showConfirmation: false, - }; - - interface State { - inToken: Token | null; - outToken: Token | null; - inputAmount: string; - acceptedQuote: Quote | undefined; - liquidityHubDisabled: boolean; - slippage: number; - forceLiquidityHub: boolean; - showConfirmation: boolean; - signature?: string; - isLiquidityHubTrade?: boolean; - } - - type Action = { type: "UPDATE"; payload: Partial } | { type: "RESET" }; - - const reducer = (state: State, action: Action): State => { - switch (action.type) { - case "UPDATE": - return { ...state, ...action.payload }; - case "RESET": - return initialState; - default: - return state; - } - }; - - interface ContextType { - state: State; - updateState: (payload: Partial) => void; - resetState: () => void; - sdk: LiquidityHubSDK; - parsedInputAmount?: string; + inToken: null, + outToken: null, + inputAmount: "", + acceptedQuote: undefined, + liquidityHubDisabled: false, + forceLiquidityHub: false, + showConfirmation: false, +}; + +interface State { + inToken: Token | null; + outToken: Token | null; + inputAmount: string; + acceptedQuote: Quote | undefined; + liquidityHubDisabled: boolean; + forceLiquidityHub: boolean; + showConfirmation: boolean; + signature?: string; + isLiquidityHubTrade?: boolean; +} + +type Action = { type: "UPDATE"; payload: Partial } | { type: "RESET" }; + +const reducer = (state: State, action: Action): State => { + switch (action.type) { + case "UPDATE": + return { ...state, ...action.payload }; + case "RESET": + return initialState; + default: + return state; } - - const Context = createContext({} as ContextType); - export const useLiquidityHubSwapContext = () => { - return useContext(Context); - }; - +}; + +interface ContextType { + state: State; + updateState: (payload: Partial) => void; + resetState: () => void; + sdk: LiquidityHubSDK; + parsedInputAmount?: string; +} + +const Context = createContext({} as ContextType); +export const useLiquidityHubSwapContext = () => { + return useContext(Context); +}; + +export const LiquidityHubSwapProvider = ({ + children, +}: { + children: ReactNode; +}) => { + const [_state, dispatch] = useReducer(reducer, initialState); + const defaultTokens = useDefaultTokens(); + + const state = useMemo(() => { + return { + ..._state, + inToken: _state.inToken || defaultTokens?.inToken || null, + outToken: _state.outToken || defaultTokens?.outToken || null, + }; + }, [_state, defaultTokens]); + + const { chainId } = useAccount(); + + const parsedInputAmount = useToRawAmount( + state.inputAmount, + state.inToken?.decimals + ); + + const updateState = useCallback( + (payload: Partial) => { + dispatch({ type: "UPDATE", payload }); + }, + [dispatch] + ); + + const resetState = useCallback(() => { + dispatch({ type: "RESET" }); + }, [dispatch]); + + const sdk = useMemo( + () => constructSDK({ partner: "widget", chainId }), + [chainId] + ); - export const LiquidityHubSwapProvider = ({ children }: { children: ReactNode }) => { - const [state, dispatch] = useReducer(reducer, initialState); - const { chainId } = useAccount(); - const { tokensWithBalances, refetch: refetchBalances } = - useTokensWithBalances(); - const parsedInputAmount = useToRawAmount( - state.inputAmount, - state.inToken?.decimals - ); - - const updateState = useCallback( - (payload: Partial) => { - dispatch({ type: "UPDATE", payload }); - }, - [dispatch] - ); - - const resetState = useCallback(() => { - dispatch({ type: "RESET" }); - refetchBalances(); - }, [dispatch, refetchBalances]); - - const sdk = useMemo( - () => constructSDK({ partner: "widget", chainId }), - [chainId] - ); - - useDefaultTokens({ - inToken: state.inToken, - outToken: state.outToken, - tokensWithBalances, - setInToken: (token) => updateState({ inToken: token }), - setOutToken: (token) => updateState({ outToken: token }), - }); - - return ( - - {children} - - ); - }; \ No newline at end of file + return ( + + {children} + + ); +}; diff --git a/src/trade/liquidity-hub/hooks.ts b/src/trade/liquidity-hub/hooks.ts index 54a8e34..5f3ede3 100644 --- a/src/trade/liquidity-hub/hooks.ts +++ b/src/trade/liquidity-hub/hooks.ts @@ -5,10 +5,10 @@ import { useInputError, getMinAmountOut, useGetRequiresApproval, - network, networks, isNativeAddress, } from "@/lib"; +import { useAppState } from "@/store"; import { permit2Address } from "@orbs-network/liquidity-hub-sdk"; import { useQueryClient, useQuery } from "@tanstack/react-query"; import { useMemo, useCallback } from "react"; @@ -19,9 +19,7 @@ import { useLiquidityHubSwapContext } from "./context"; export const QUOTE_REFETCH_INTERVAL = 20_000; export const useParaswapMinAmountOut = () => { - const { - state: { slippage }, - } = useLiquidityHubSwapContext(); + const { slippage } = useAppState(); const optimalRate = useOptimalRate().data; return useMemo(() => { return getMinAmountOut(slippage, optimalRate?.destAmount || "0"); @@ -31,8 +29,10 @@ export const useParaswapMinAmountOut = () => { export function useLiquidityHubQuote() { const queryClient = useQueryClient(); const { chainId, address: account } = useAccount(); + const { slippage } = useAppState(); + const { - state: { inToken, outToken, liquidityHubDisabled, slippage }, + state: { inToken, outToken, liquidityHubDisabled }, sdk, parsedInputAmount, } = useLiquidityHubSwapContext(); @@ -144,6 +144,7 @@ export const useLiquidityHubInputError = () => { const { state: { inToken, inputAmount }, } = useLiquidityHubSwapContext(); + return useInputError({ inputAmount, inToken, diff --git a/src/trade/liquidity-hub/liquidity-hub-confirmation-dialog.tsx b/src/trade/liquidity-hub/liquidity-hub-confirmation-dialog.tsx index 80d8237..f5e1a78 100644 --- a/src/trade/liquidity-hub/liquidity-hub-confirmation-dialog.tsx +++ b/src/trade/liquidity-hub/liquidity-hub-confirmation-dialog.tsx @@ -10,6 +10,7 @@ import { getErrorMessage, getLiquidityProviderName, getSteps, + isNativeAddress, promiseWithTimeout, toExactAmount, useParaswapBuildTxCallback, @@ -39,7 +40,8 @@ import { _TypedDataEncoder } from "@ethersproject/hash"; import { permit2Address, Quote } from "@orbs-network/liquidity-hub-sdk"; import { TransactionParams } from "@paraswap/sdk"; import { toast } from "sonner"; -import { useToExactAmount } from "../hooks"; +import { useNetwork, useToExactAmount } from "../hooks"; +import { useAppState } from "@/store"; // Construct steps for swap to display in UI const useSteps = (steps?: number[]) => { @@ -107,9 +109,9 @@ export function LiquidityHubConfirmationDialog({ const approvalLoading = isLiquidityHubTrade ? liquidityHubApproval.approvalLoading : paraswapApproval.approvalLoading; + const onSubmit = useCallback(async () => { if (!isLiquidityHubTrade) { - console.log("Proceeding with Liquidity Hub"); swapWithParaswap(); } else { swapWithLiquidityHub(); @@ -241,23 +243,24 @@ const Details = () => { const { state: { outToken, isLiquidityHubTrade }, } = useLiquidityHubSwapContext(); - const outAmountUsd = optimalRate?.destUSD; - const outAmount = isLiquidityHubTrade - ? quote?.outAmount - : optimalRate?.destAmount; + const outTokenUsd = usePriceUsd(outToken?.address).data; const gasPrice = useMemo(() => { - const gasAmountOut = isLiquidityHubTrade - ? toExactAmount(quote?.gasAmountOut, outToken?.decimals) - : BN(optimalRate?.gasCost || 0) - .dividedBy(1e18) - .toString(); - - if (!outAmountUsd || !gasAmountOut || !outToken) return 0; - const gas = toExactAmount(gasAmountOut, outToken.decimals); - const usd = Number(outAmountUsd) / Number(outAmount); - return Number(gas) * usd; - }, [outAmountUsd, outToken, outAmount, quote, isLiquidityHubTrade]); + if (!isLiquidityHubTrade) { + return Number(optimalRate?.gasCostUSD || "0"); + } + + if (!outToken || !outTokenUsd) return 0; + const gas = toExactAmount(quote?.gasAmountOut, outToken.decimals); + + return Number(gas) * outTokenUsd; + }, [ + isLiquidityHubTrade, + optimalRate?.gasCostUSD, + outToken, + outTokenUsd, + quote?.gasAmountOut, + ]); return (
@@ -293,10 +296,12 @@ export const useParaswapSwapCallback = ( const buildParaswapTxCallback = useParaswapBuildTxCallback(); const optimalRate = useOptimalRate().data; const { - state: { slippage, inToken }, + state: { inToken }, } = useLiquidityHubSwapContext(); + const { slippage } = useAppState(); + const wToken = useNetwork()?.wToken.address; const requiresApproval = useParaswapApproval().requiresApproval; - + const { address } = useAccount(); return useMutation({ @@ -312,6 +317,9 @@ export const useParaswapSwapCallback = ( if (!optimalRate) { throw new Error("No optimal rate found"); } + if (!wToken) { + throw new Error("WToken not found"); + } try { updateSwapProgressState({ swapStatus: SwapStatus.LOADING }); @@ -326,7 +334,7 @@ export const useParaswapSwapCallback = ( updateSwapProgressState({ currentStep: SwapSteps.Approve }); await approveAllowance( address, - optimalRate.srcToken, + isNativeAddress(inToken.address) ? wToken : inToken.address, optimalRate.tokenTransferProxy as Address ); } @@ -453,7 +461,7 @@ async function signTransaction(quote: Quote, analyticsEvents: AnalyticsEvents) { // Sign transaction and get signature const signature = await promiseWithTimeout( - signTypedData(wagmiConfig, payload), + (signTypedData as any)(wagmiConfig, payload), 40_000 ); @@ -476,9 +484,11 @@ export function useLiquidityHubSwapCallback( ) { const { sdk: liquidityHub, - state: { inToken, slippage }, + state: { inToken }, updateState, } = useLiquidityHubSwapContext(); + const { slippage } = useAppState(); + const buildParaswapTxCallback = useParaswapBuildTxCallback(); const optimalRate = useOptimalRate().data; const { getLatestQuote, data: quote } = useLiquidityHubQuote(); diff --git a/src/trade/liquidity-hub/liquidity-hub-swap.tsx b/src/trade/liquidity-hub/liquidity-hub-swap.tsx index f9780d3..9f8322f 100644 --- a/src/trade/liquidity-hub/liquidity-hub-swap.tsx +++ b/src/trade/liquidity-hub/liquidity-hub-swap.tsx @@ -6,24 +6,9 @@ import { Button } from "@/components/ui/button"; import { _TypedDataEncoder } from "@ethersproject/hash"; import { SwapStatus } from "@orbs-network/swap-ui"; import { Token } from "@/types"; -import { - ErrorCodes, - format, - getLiquidityProviderName, - getMinAmountOut, - getQuoteErrorMessage, - toExactAmount, -} from "@/lib"; +import { ErrorCodes } from "@/lib"; import "../style.css"; import { useConnectModal } from "@rainbow-me/rainbowkit"; -import { SettingsIcon } from "lucide-react"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover"; -import { Label } from "@/components/ui/label"; -import { Input } from "@/components/ui/input"; import BN from "bignumber.js"; import { LiquidityHubSwapProvider, @@ -37,8 +22,7 @@ import { useOptimalRate, useParaswapMinAmountOut, } from "./hooks"; -import { DataDetails } from "@/components/ui/data-details"; -import { Separator } from "@radix-ui/react-dropdown-menu"; +import { SwapDetails } from "./swap-details"; export const useIsLiquidityHubTrade = () => { const { @@ -47,8 +31,6 @@ export const useIsLiquidityHubTrade = () => { const liquidityHubQuote = useLiquidityHubQuote().data; const paraswapMinAmountOut = useParaswapMinAmountOut(); - console.log(liquidityHubQuote?.minAmountOut, paraswapMinAmountOut); - return useMemo(() => { // Choose between liquidity hub and dex swap based on the min amount out if (liquidityHubDisabled) return false; @@ -64,28 +46,11 @@ export const useIsLiquidityHubTrade = () => { }; function SwapPanel() { - const { - state: { inToken, outToken }, - updateState, - } = useLiquidityHubSwapContext(); - - // Handle Token Switch - const handleSwitch = useCallback(() => { - updateState({ - inToken: outToken, - outToken: inToken, - inputAmount: "", - }); - }, [inToken, outToken, updateState]); - return (
-
-
- -
+ @@ -94,40 +59,23 @@ function SwapPanel() { ); } -const Settings = () => { +const Switch = () => { const { - state: { slippage }, + state: { inToken, outToken }, updateState, } = useLiquidityHubSwapContext(); + + const handleSwitch = useCallback(() => { + updateState({ + inToken: outToken, + outToken: inToken, + inputAmount: "", + }); + }, [inToken, outToken, updateState]); + return ( -
- - - - - -
-
- -
- - updateState({ slippage: e.target.valueAsNumber }) - } - value={slippage} - step={0.1} - className="text-right w-16 [&::-webkit-inner-spin-button]:appearance-none p-2 h-7" - /> -
%
-
-
-
-
-
+
+
); }; @@ -138,6 +86,7 @@ const ConfirmationModal = () => { const { data: liquidityHubQuote } = useLiquidityHubQuote(); const { data: optimalRate, isLoading: optimalRateLoading } = useOptimalRate(); const inputError = useLiquidityHubInputError(); + const isLiquidityHubTrade = useIsLiquidityHubTrade(); const { state: { inputAmount }, @@ -157,11 +106,6 @@ const ConfirmationModal = () => { text: "Enter amount", }; } - if (inputAmount && optimalRateLoading) { - return { - text: "Fetching quote...", - }; - } if (!optimalRate && inputAmount) { return { @@ -191,7 +135,7 @@ const ConfirmationModal = () => { setIsOpen(false); updateState({ isLiquidityHubTrade: false }); if (status === SwapStatus.SUCCESS) { - updateState({inputAmount: ''}); + updateState({ inputAmount: "" }); } }, [resetState] @@ -215,61 +159,6 @@ const ConfirmationModal = () => { ); }; -export function SwapDetails() { - const isLiquidityHubTrade = useIsLiquidityHubTrade(); - const optimalRate = useOptimalRate().data; - const account = useAccount().address; - const { - state: { outToken, inToken }, - } = useLiquidityHubSwapContext(); - const minAmountOut = useParaswapMinAmountOut(); - const inPriceUsd = useMemo(() => { - if (!optimalRate) return 0; - const amount = toExactAmount(optimalRate.srcAmount, inToken?.decimals); - return Number(optimalRate.srcUSD) / Number(amount); - }, [optimalRate, inToken]); - - const minOutAmount = useToExactAmount(minAmountOut, outToken?.decimals); - const outAmount = useToExactAmount( - optimalRate?.destAmount, - outToken?.decimals - ); - - const outPriceUsd = useMemo(() => { - if (!optimalRate) return 0; - const amount = toExactAmount(optimalRate.destAmount, outToken?.decimals); - return Number(optimalRate.destUSD) / Number(amount); - }, [optimalRate, outToken]); - - if (!inToken || !outToken || !account || !optimalRate) return null; - - const rate = inPriceUsd / outPriceUsd; - - let data: Record = { - Rate: `1 ${inToken.symbol} ≈ ${format.crypto(rate)} ${outToken.symbol}`, - }; - - data = { - ...data, - "Est. Received": `${format.crypto(Number(outAmount))} ${outToken.symbol}`, - "Min. Received": `${format.crypto(Number(minOutAmount))} ${ - outToken.symbol - }`, - "Routing source": getLiquidityProviderName(isLiquidityHubTrade), - }; - - return ( -
- - -
-
Recepient
-
{format.address(account)}
-
-
- ); -} - const InTokenCard = () => { const { state: { inputAmount, inToken }, @@ -336,7 +225,7 @@ const OutTokenCard = () => { ); }; -export const Swap = () => { +export const SwapLiquidityHub = () => { return ( diff --git a/src/trade/liquidity-hub/swap-details.tsx b/src/trade/liquidity-hub/swap-details.tsx new file mode 100644 index 0000000..059eb23 --- /dev/null +++ b/src/trade/liquidity-hub/swap-details.tsx @@ -0,0 +1,64 @@ +import { DataDetails } from "@/components/ui/data-details"; +import { toExactAmount, getLiquidityProviderName, format } from "@/lib"; +import { Separator } from "@radix-ui/react-dropdown-menu"; +import { useMemo } from "react"; +import { useAccount } from "wagmi"; +import { useToExactAmount } from "../hooks"; +import { useLiquidityHubSwapContext } from "./context"; +import { useOptimalRate, useParaswapMinAmountOut } from "./hooks"; +import { useIsLiquidityHubTrade } from "./liquidity-hub-swap"; + + export function SwapDetails() { + const isLiquidityHubTrade = useIsLiquidityHubTrade(); + const optimalRate = useOptimalRate().data; + const account = useAccount().address; + const { + state: { outToken, inToken }, + } = useLiquidityHubSwapContext(); + const minAmountOut = useParaswapMinAmountOut(); + const inPriceUsd = useMemo(() => { + if (!optimalRate) return 0; + const amount = toExactAmount(optimalRate.srcAmount, inToken?.decimals); + return Number(optimalRate.srcUSD) / Number(amount); + }, [optimalRate, inToken]); + + const minOutAmount = useToExactAmount(minAmountOut, outToken?.decimals); + const outAmount = useToExactAmount( + optimalRate?.destAmount, + outToken?.decimals + ); + + const outPriceUsd = useMemo(() => { + if (!optimalRate) return 0; + const amount = toExactAmount(optimalRate.destAmount, outToken?.decimals); + return Number(optimalRate.destUSD) / Number(amount); + }, [optimalRate, outToken]); + + if (!inToken || !outToken || !account || !optimalRate) return null; + + const rate = inPriceUsd / outPriceUsd; + + let data: Record = { + Rate: `1 ${inToken.symbol} ≈ ${format.crypto(rate)} ${outToken.symbol}`, + }; + + data = { + ...data, + "Est. Received": `${format.crypto(Number(outAmount))} ${outToken.symbol}`, + "Min. Received": `${format.crypto(Number(minOutAmount))} ${ + outToken.symbol + }`, + "Routing source": getLiquidityProviderName(isLiquidityHubTrade), + }; + + return ( +
+ + +
+
Recepient
+
{format.address(account)}
+
+
+ ); + } \ No newline at end of file diff --git a/src/trade/settings.tsx b/src/trade/settings.tsx new file mode 100644 index 0000000..7f93379 --- /dev/null +++ b/src/trade/settings.tsx @@ -0,0 +1,43 @@ +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { useAppState } from "@/store"; +import { SettingsIcon } from "lucide-react"; + +export const Settings = () => { + const { slippage, setSlippage } = useAppState(); + return ( +
+ + + + + +
+
+ +
+ setSlippage(e.target.valueAsNumber)} + value={slippage} + step={0.1} + className="text-right w-16 [&::-webkit-inner-spin-button]:appearance-none p-2 h-7" + /> +
%
+
+
+
+
+
+
+ ); +}; diff --git a/src/trade/trade.tsx b/src/trade/trade.tsx deleted file mode 100644 index aa8f880..0000000 --- a/src/trade/trade.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' -import { Swap } from './liquidity-hub/liquidity-hub-swap' -import { Twap } from './twap/twap' - -export function Trade() { - return ( -
-

Trade

- - - - Swap - TWAP - Limit - - - - - - - - - - - -
- ) -} diff --git a/src/trade/twap/components/inputs.tsx b/src/trade/twap/components/inputs.tsx index e5819f4..6908c5f 100644 --- a/src/trade/twap/components/inputs.tsx +++ b/src/trade/twap/components/inputs.tsx @@ -12,7 +12,7 @@ import { TimeUnit } from "@orbs-network/twap-sdk"; import { useMemo } from "react"; import { NumericFormat } from "react-number-format"; import { useDerivedTwapSwapData } from "../hooks"; -import { useTwapContext } from "../twap-context"; +import { useTwapContext } from "../context"; const options: { text: string; unit: TimeUnit }[] = [ { diff --git a/src/trade/twap/components/limit-price-input.tsx b/src/trade/twap/components/limit-price-input.tsx index 0a17a62..2357ede 100644 --- a/src/trade/twap/components/limit-price-input.tsx +++ b/src/trade/twap/components/limit-price-input.tsx @@ -2,11 +2,9 @@ import { Token } from "@/types"; import { NumericFormat } from "react-number-format"; import { format, - networks, toExactAmount, toRawAmount, usePriceUsd, - useTokensWithBalances, } from "@/lib"; import { Card } from "@/components/ui/card"; import { Skeleton } from "@/components/ui/skeleton"; @@ -16,7 +14,7 @@ import { Button } from "@/components/ui/button"; import BN from "bignumber.js"; import { ArrowUpDown, X } from "lucide-react"; import { Avatar, AvatarImage } from "@/components/ui/avatar"; -import { useTwapContext } from "../twap-context"; +import { useTwapContext } from "../context"; import { useMarketPrice, useTradePrice } from "../hooks"; const useMarketPriceUI = () => { diff --git a/src/trade/twap/components/price-toggle.tsx b/src/trade/twap/components/price-toggle.tsx index 32c9c14..6fdb938 100644 --- a/src/trade/twap/components/price-toggle.tsx +++ b/src/trade/twap/components/price-toggle.tsx @@ -1,6 +1,6 @@ import { Switch } from "@/components/ui/switch"; import{ useCallback } from "react"; -import { useTwapContext } from "../twap-context"; +import { useTwapContext } from "../context"; export function PriceToggle() { const { diff --git a/src/trade/twap/components/src-chunk-size.tsx b/src/trade/twap/components/src-chunk-size.tsx index 7866f23..3c7d472 100644 --- a/src/trade/twap/components/src-chunk-size.tsx +++ b/src/trade/twap/components/src-chunk-size.tsx @@ -1,7 +1,7 @@ import { usePriceUsd } from "@/lib"; import { useToExactAmount } from "@/trade/hooks"; import { useDerivedTwapSwapData } from "../hooks"; -import { useTwapContext } from "../twap-context"; +import { useTwapContext } from "../context"; export function SrcChunkSize() { const { state } = useTwapContext(); diff --git a/src/trade/twap/twap-context.tsx b/src/trade/twap/context.tsx similarity index 89% rename from src/trade/twap/twap-context.tsx rename to src/trade/twap/context.tsx index 29a9e53..a73686f 100644 --- a/src/trade/twap/twap-context.tsx +++ b/src/trade/twap/context.tsx @@ -1,3 +1,4 @@ +import { useDefaultTokens } from "@/lib"; import { Token } from "@/types"; import { Configs, @@ -61,12 +62,24 @@ type TwapState = { const initialState = {typedAmount: ''} as TwapState; const useTwapState = () => { - const [values, dispatch] = useReducer( + const [_values, dispatch] = useReducer( (state: TwapState, action: Action) => reducer(state, action, initialState), initialState ); + const defaultTokens = useDefaultTokens(); + + + const values = useMemo(() => { + return { + ..._values, + inToken: _values.inToken || defaultTokens?.inToken || null, + outToken: _values.outToken || defaultTokens?.outToken || null, + } + }, [_values, defaultTokens?.inToken, defaultTokens?.outToken]) + + const updateState = useCallback( (payload: Partial) => { dispatch({ type: "UPDATE_STATE", payload }); diff --git a/src/trade/twap/hooks.ts b/src/trade/twap/hooks.ts index d2c323f..d012b23 100644 --- a/src/trade/twap/hooks.ts +++ b/src/trade/twap/hooks.ts @@ -5,7 +5,7 @@ import { usePriceUsd, } from "@/lib"; import { useMemo } from "react"; -import { useTwapContext } from "./twap-context"; +import { useTwapContext } from "./context"; import BN from "bignumber.js"; import { useToExactAmount, useToRawAmount } from "../hooks"; import { diff --git a/src/trade/twap/orders/orders.tsx b/src/trade/twap/orders/orders.tsx index d346732..fb24845 100644 --- a/src/trade/twap/orders/orders.tsx +++ b/src/trade/twap/orders/orders.tsx @@ -18,7 +18,7 @@ import { eqIgnoreCase, format, makeElipsisAddress, - useTokensList, + useSortedTokens, wagmiConfig, waitForConfirmations, } from "@/lib"; @@ -33,7 +33,7 @@ import { useGroupedOrders, useOrdersQuery } from "./use-orders-query"; import { useExplorer, useToExactAmount } from "@/trade/hooks"; import moment from "moment"; import { useMutation } from "@tanstack/react-query"; -import { useTwapContext } from "../twap-context"; +import { useTwapContext } from "../context"; import { writeContract, simulateContract, @@ -118,11 +118,11 @@ const OrdersMenu = () => { }; const useToken = (tokenAddress?: string) => { - const { data } = useTokensList(); + const tokens = useSortedTokens() return useMemo( - () => data?.find((it) => eqIgnoreCase(it.address, tokenAddress || "")), - [data, tokenAddress] + () => tokens?.find((it) => eqIgnoreCase(it.address, tokenAddress || "")), + [tokens, tokenAddress] ); }; diff --git a/src/trade/twap/orders/use-orders-query.ts b/src/trade/twap/orders/use-orders-query.ts index 8bf6649..217feb2 100644 --- a/src/trade/twap/orders/use-orders-query.ts +++ b/src/trade/twap/orders/use-orders-query.ts @@ -2,7 +2,7 @@ import { groupOrdersByStatus } from "@orbs-network/twap-sdk"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMemo } from "react"; import { useAccount } from "wagmi"; -import { useTwapContext } from "../twap-context"; +import { useTwapContext } from "../context"; const useOrdersQueryKey = () => { const { address } = useAccount(); diff --git a/src/trade/twap/twap-confirmation-dialog.tsx b/src/trade/twap/twap-confirmation-dialog.tsx index bf162a4..46afa3c 100644 --- a/src/trade/twap/twap-confirmation-dialog.tsx +++ b/src/trade/twap/twap-confirmation-dialog.tsx @@ -8,7 +8,7 @@ import { useInTokenUsd, useOutTokenUsd, } from "./hooks"; -import { useTwapContext } from "./twap-context"; +import { useTwapContext } from "./context"; import { format, resolveNativeTokenAddress, diff --git a/src/trade/twap/twap.tsx b/src/trade/twap/twap.tsx index 5b14fb7..0ce5a53 100644 --- a/src/trade/twap/twap.tsx +++ b/src/trade/twap/twap.tsx @@ -3,27 +3,15 @@ import { SwitchButton } from "@/components/ui/switch-button"; import { useCallback, useState } from "react"; import { useAccount } from "wagmi"; import { Button } from "@/components/ui/button"; -import { - useDefaultTokens, - useTokensWithBalances, - useTokenBalance, -} from "@/lib"; +import { useTokenBalaces } from "@/lib"; import "../style.css"; import { useConnectModal } from "@rainbow-me/rainbowkit"; -import { SettingsIcon } from "lucide-react"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover"; -import { Label } from "@/components/ui/label"; -import { Input } from "@/components/ui/input"; import { LimitPriceInput } from "./components/limit-price-input"; import { TwapContextProvider, useTwapContext, useTwapStateActions, -} from "./twap-context"; +} from "./context"; import { useDerivedTwapSwapData, useInputLabels, @@ -40,29 +28,17 @@ import { SwapStatus } from "@orbs-network/swap-ui"; import { Orders } from "./orders/orders"; export function Panel() { - const { tokensWithBalances, refetch: refetchBalances } = - useTokensWithBalances(); - const [slippage, setSlippage] = useState(0.5); + const { refetch: refetchBalances } = useTokenBalaces(); // Get wagmi account const account = useAccount(); const { state } = useTwapContext(); const { - updateState, resetState, values: { inToken, outToken, typedAmount }, } = state; - // Set Initial Tokens - const defaultTokens = useDefaultTokens({ - inToken, - outToken, - tokensWithBalances, - setInToken: (token) => updateState({ inToken: token }), - setOutToken: (token) => updateState({ outToken: token }), - }); - const inputError = useTwapInputError(); const { setInToken, setInputAmount, setOutToken, onSwitchTokens } = useTwapStateActions(); @@ -92,33 +68,6 @@ export function Panel() { const amountLoading = Boolean(inToken && !marketPrice); return (
-
- - - - - -
-
- -
- setSlippage(e.target.valueAsNumber)} - value={slippage} - step={0.1} - className="text-right w-16 [&::-webkit-inner-spin-button]:appearance-none p-2 h-7" - /> -
%
-
-
-
-
-
-
@@ -126,7 +75,7 @@ export function Panel() { label={inputLabel} amount={typedAmount} amountUsd={inAmountUsd} - selectedToken={inToken || defaultTokens[0]} + selectedToken={inToken} onSelectToken={setInToken} onValueChange={setInputAmount} inputError={inputError} @@ -138,7 +87,7 @@ export function Panel() { label={outputLabel} amount={destAmount ?? ""} amountUsd={outAmountUsd} - selectedToken={outToken || defaultTokens[1]} + selectedToken={outToken} onSelectToken={setOutToken} isAmountEditable={false} amountLoading={amountLoading} @@ -167,10 +116,15 @@ export function Panel() { ); } -export const Twap = ({ isLimitPanel }: { isLimitPanel?: boolean }) => { +export const SwapTwap = ({ isLimitPanel }: { isLimitPanel?: boolean }) => { return ( ); }; + + +export const SwapLimit = () => { + return +} \ No newline at end of file diff --git a/tsconfig.app.tsbuildinfo b/tsconfig.app.tsbuildinfo index f7a97e0..c14af47 100644 --- a/tsconfig.app.tsbuildinfo +++ b/tsconfig.app.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/app.tsx","./src/main.tsx","./src/root.tsx","./src/types.ts","./src/vite-env.d.ts","./src/components/header.tsx","./src/components/order-details.tsx","./src/components/spinner.tsx","./src/components/swap-details.tsx","./src/components/theme-toggle.tsx","./src/components/tokens/token-card.tsx","./src/components/tokens/token-select.tsx","./src/components/ui/avatar.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/data-details.tsx","./src/components/ui/dialog.tsx","./src/components/ui/dropdown-menu.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/popover.tsx","./src/components/ui/separator.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/sonner.tsx","./src/components/ui/switch-button.tsx","./src/components/ui/switch.tsx","./src/components/ui/tabs.tsx","./src/components/ui/tooltip.tsx","./src/lib/abis.ts","./src/lib/approveallowance.ts","./src/lib/getrequiresapproval.ts","./src/lib/index.ts","./src/lib/networks.ts","./src/lib/usebalances.ts","./src/lib/usedebounce.ts","./src/lib/usedefaulttokens.ts","./src/lib/usegetrequiresapproval.ts","./src/lib/usehandleinputerror.ts","./src/lib/useparaswap.ts","./src/lib/usepriceusd.ts","./src/lib/usetokenlist.ts","./src/lib/usetokenswithbalances.ts","./src/lib/usewraporunwraponly.ts","./src/lib/utils.ts","./src/lib/wagmi-config.ts","./src/lib/wraptoken.ts","./src/providers/rainbow-provider.tsx","./src/providers/theme-provider.tsx","./src/trade/hooks.ts","./src/trade/swap-confirmation-dialog.tsx","./src/trade/trade.tsx","./src/trade/use-swap-state.ts","./src/trade/liquidity-hub/liquidity-hub-confirmation-dialog.tsx","./src/trade/liquidity-hub/liquidity-hub-swap.tsx","./src/trade/liquidity-hub/useliquidityhubquote.ts","./src/trade/liquidity-hub/useliquidityhubsdk.ts","./src/trade/liquidity-hub/useliquidityhubswapcallback.ts","./src/trade/twap/hooks.ts","./src/trade/twap/twap-confirmation-dialog.tsx","./src/trade/twap/twap-context.tsx","./src/trade/twap/twap.tsx","./src/trade/twap/utils.ts","./src/trade/twap/components/inputs.tsx","./src/trade/twap/components/limit-price-input.tsx","./src/trade/twap/components/price-toggle.tsx","./src/trade/twap/components/src-chunk-size.tsx","./src/trade/twap/orders/orders-context.tsx","./src/trade/twap/orders/orders.tsx","./src/trade/twap/orders/use-orders-query.ts"],"version":"5.6.3"} \ No newline at end of file +{"root":["./src/app.tsx","./src/main.tsx","./src/root.tsx","./src/store.ts","./src/types.ts","./src/vite-env.d.ts","./src/components/header.tsx","./src/components/order-details.tsx","./src/components/spinner.tsx","./src/components/theme-toggle.tsx","./src/components/tokens/token-card.tsx","./src/components/tokens/token-select.tsx","./src/components/ui/avatar.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/data-details.tsx","./src/components/ui/dialog.tsx","./src/components/ui/dropdown-menu.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/popover.tsx","./src/components/ui/separator.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/sonner.tsx","./src/components/ui/switch-button.tsx","./src/components/ui/switch.tsx","./src/components/ui/tabs.tsx","./src/components/ui/tooltip.tsx","./src/lib/abis.ts","./src/lib/approveallowance.ts","./src/lib/getrequiresapproval.ts","./src/lib/index.ts","./src/lib/networks.ts","./src/lib/usedebounce.ts","./src/lib/usedefaulttokens.ts","./src/lib/usegetrequiresapproval.ts","./src/lib/usehandleinputerror.ts","./src/lib/useparaswap.ts","./src/lib/usepriceusd.ts","./src/lib/usetokens.ts","./src/lib/usewraporunwraponly.ts","./src/lib/utils.ts","./src/lib/wagmi-config.ts","./src/lib/wraptoken.ts","./src/providers/rainbow-provider.tsx","./src/providers/theme-provider.tsx","./src/trade/hooks.ts","./src/trade/settings.tsx","./src/trade/swap-confirmation-dialog.tsx","./src/trade/liquidity-hub/context.tsx","./src/trade/liquidity-hub/hooks.ts","./src/trade/liquidity-hub/liquidity-hub-confirmation-dialog.tsx","./src/trade/liquidity-hub/liquidity-hub-swap.tsx","./src/trade/liquidity-hub/swap-details.tsx","./src/trade/twap/context.tsx","./src/trade/twap/hooks.ts","./src/trade/twap/twap-confirmation-dialog.tsx","./src/trade/twap/twap.tsx","./src/trade/twap/utils.ts","./src/trade/twap/components/inputs.tsx","./src/trade/twap/components/limit-price-input.tsx","./src/trade/twap/components/price-toggle.tsx","./src/trade/twap/components/src-chunk-size.tsx","./src/trade/twap/orders/orders-context.tsx","./src/trade/twap/orders/orders.tsx","./src/trade/twap/orders/use-orders-query.ts"],"version":"5.6.3"} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 883989b..156aa9d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -983,10 +983,10 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@orbs-network/liquidity-hub-sdk@^1.0.41": - version "1.0.41" - resolved "https://registry.yarnpkg.com/@orbs-network/liquidity-hub-sdk/-/liquidity-hub-sdk-1.0.41.tgz#9d19adf60df4a546217eb2f08f9691856bd8416e" - integrity sha512-dYDtvCwYI6y0zXYQs8VgrlR+XlFyxgG4a+FOtOHcHCX8ZlPyp7DiwppFl9ly4qMEKM0IFxlnny1fyOTWAHYmuA== +"@orbs-network/liquidity-hub-sdk@^1.0.44": + version "1.0.44" + resolved "https://registry.yarnpkg.com/@orbs-network/liquidity-hub-sdk/-/liquidity-hub-sdk-1.0.44.tgz#244c4e26bbeef75d48a8ca041fa730c5885e6a17" + integrity sha512-5IpPDNUVdnzv2+4MdLDFHjJQfPqn+NWj1EqBcdKgZ3YLELwYrgJ8O+38Tuy7iU1fS/OR07f7BvRyczywkGPRJA== "@orbs-network/swap-ui@^0.0.14": version "0.0.14"