diff --git a/README.md b/README.md index ef997f6792..2ff30a4aee 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,6 @@ Kwenta welcomes contributors. Regardless of the time you have available, everyon - [ethers.js v5](https://github.com/ethers-io/ethers.js) - Ethereum wallet implementation. - [Rainbowkit](https://github.com/rainbow-me/rainbowkit) - for ethereum wallet onboarding. -- [@synthetixio/contracts-interface](https://github.com/Synthetixio/js-monorepo/tree/master/packages/contracts-interface) - for interactions with the Synthetix protocol. -- [@synthetixio/queries](https://github.com/Synthetixio/js-monorepo/tree/master/packages/queries) - for historical data (powered by [TheGraph](https://thegraph.com/)) ## Development diff --git a/components/Badge/MarketBadge.tsx b/components/Badge/MarketBadge.tsx index 8fe25e52a1..2f1986e9a3 100644 --- a/components/Badge/MarketBadge.tsx +++ b/components/Badge/MarketBadge.tsx @@ -2,18 +2,16 @@ import React, { FC, memo, ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; import styled from 'styled-components'; -import { CurrencyKey } from 'constants/currency'; -import { FuturesClosureReason } from 'hooks/useFuturesMarketClosed'; import useIsMarketTransitioning from 'hooks/useIsMarketTransitioning'; -import { FuturesMarketAsset } from 'sdk/types/futures'; +import { FuturesMarketAsset, SynthSuspensionReason } from 'sdk/types/futures'; import { marketIsOpen, marketNextOpen, marketNextTransition } from 'utils/marketHours'; import Badge from './Badge'; type MarketBadgeProps = { - currencyKey: FuturesMarketAsset | null; - isFuturesMarketClosed: boolean; - futuresClosureReason?: FuturesClosureReason; + currencyKey: FuturesMarketAsset; + isFuturesMarketClosed?: boolean; + futuresClosureReason?: SynthSuspensionReason; fallbackComponent?: ReactElement; }; @@ -21,7 +19,7 @@ type TransitionBadgeProps = { isOpen: boolean; }; -export const TransitionBadge: FC = memo(({ isOpen }) => { +const TransitionBadge: FC = memo(({ isOpen }) => { const { t } = useTranslation(); return ( @@ -34,10 +32,9 @@ export const TransitionBadge: FC = memo(({ isOpen }) => { export const MarketBadge: FC = memo( ({ currencyKey, isFuturesMarketClosed, futuresClosureReason, fallbackComponent }) => { const { t } = useTranslation(); - const isOpen = marketIsOpen((currencyKey as CurrencyKey) ?? null); - - const nextOpen = marketNextOpen((currencyKey as CurrencyKey) ?? ''); - const nextTransition = marketNextTransition((currencyKey as CurrencyKey) ?? ''); + const isOpen = marketIsOpen(currencyKey); + const nextOpen = marketNextOpen(currencyKey); + const nextTransition = marketNextTransition(currencyKey); const timerSetting = isOpen === null ? null : isOpen ? nextTransition : nextOpen; const isMarketTransitioning = useIsMarketTransitioning(timerSetting ?? null); diff --git a/components/Currency/CurrencyName/CurrencyName.tsx b/components/Currency/CurrencyName/CurrencyName.tsx index 122a322b63..2dca1da306 100644 --- a/components/Currency/CurrencyName/CurrencyName.tsx +++ b/components/Currency/CurrencyName/CurrencyName.tsx @@ -4,7 +4,7 @@ import styled, { css } from 'styled-components'; import { ContainerRowMixin } from 'components/layout/grid'; import MarketClosureIcon from 'components/MarketClosureIcon'; -import { MarketClosureReason } from 'hooks/useMarketClosed'; +import { MarketClosureReason } from 'sdk/types/futures'; import CurrencyIcon from '../CurrencyIcon'; import { CurrencyIconProps } from '../CurrencyIcon/CurrencyIcon'; diff --git a/components/MarketClosureIcon/MarketClosureIcon.tsx b/components/MarketClosureIcon/MarketClosureIcon.tsx index 30d972ef04..7ac3320c9e 100644 --- a/components/MarketClosureIcon/MarketClosureIcon.tsx +++ b/components/MarketClosureIcon/MarketClosureIcon.tsx @@ -4,10 +4,10 @@ import CircuitBreakerIcon from 'assets/svg/app/market-closure/circuit-breaker.sv import EmergencyShutdownIcon from 'assets/svg/app/market-closure/emergency-shutdown.svg'; import FrozenIcon from 'assets/svg/app/market-closure/frozen.svg'; import MarketPauseIcon from 'assets/svg/app/market-closure/market-pause.svg'; -import { MarketClosureReason } from 'hooks/useMarketClosed'; +import { MarketClosureReason } from 'sdk/types/futures'; type MarketClosureIconProps = { - marketClosureReason: MarketClosureReason; + marketClosureReason: MarketClosureReason | 'frozen'; size?: 'sm' | 'lg'; }; diff --git a/components/TVChart/TVChart.tsx b/components/TVChart/TVChart.tsx index f5599f0658..7fa0bb0869 100644 --- a/components/TVChart/TVChart.tsx +++ b/components/TVChart/TVChart.tsx @@ -1,10 +1,10 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; import { useRouter } from 'next/router'; import { useRef, useContext, useEffect, useCallback, useMemo } from 'react'; import { ThemeContext } from 'styled-components'; import Connector from 'containers/Connector'; import { chain } from 'containers/Connector/config'; +import { NetworkId } from 'sdk/types/common'; import { FuturesOrder } from 'sdk/types/futures'; import { PricesListener } from 'sdk/types/prices'; import { ChartBody } from 'sections/exchange/TradeCard/Charts/common/styles'; diff --git a/constants/currency.ts b/constants/currency.ts index 9b31e484c9..8501d23052 100644 --- a/constants/currency.ts +++ b/constants/currency.ts @@ -1,9 +1,7 @@ -import { CurrencyKey } from '@synthetixio/contracts-interface'; import Wei from '@synthetixio/wei'; import keyBy from 'lodash/keyBy'; -export type { CurrencyKey } from '@synthetixio/contracts-interface'; -export { Synths } from '@synthetixio/contracts-interface'; +export type CurrencyKey = string; // TODO: standardize this export type Category = 'crypto' | 'forex' | 'equities' | 'index' | 'commodity' | 'inverse'; diff --git a/constants/defaults.ts b/constants/defaults.ts index eb702e379f..07ba78dabc 100644 --- a/constants/defaults.ts +++ b/constants/defaults.ts @@ -1,12 +1,10 @@ -import { NetworkIdByName } from '@synthetixio/contracts-interface'; - import { Language } from 'translations/constants'; // app defaults export const DEFAULT_LANGUAGE: Language = Language.EN; // network defaults -export const DEFAULT_NETWORK_ID = NetworkIdByName['mainnet-ovm']; +export const DEFAULT_NETWORK_ID = 10; export const DEFAULT_GAS_BUFFER = 5000; export const DEFAULT_GAS_LIMIT = 500000; diff --git a/constants/queryKeys.ts b/constants/queryKeys.ts index 9d6c187804..2c98a8f2bf 100644 --- a/constants/queryKeys.ts +++ b/constants/queryKeys.ts @@ -1,8 +1,6 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; - -import { FuturesAccountType } from 'queries/futures/types'; import { Period } from 'sdk/constants/period'; -import { FuturesMarketAsset } from 'sdk/types/futures'; +import { NetworkId } from 'sdk/types/common'; +import { FuturesAccountType, FuturesMarketAsset } from 'sdk/types/futures'; import { CurrencyKey } from './currency'; diff --git a/constants/routes.ts b/constants/routes.ts index 1115cd7830..7ec9ce9b54 100644 --- a/constants/routes.ts +++ b/constants/routes.ts @@ -1,5 +1,4 @@ -import { FuturesAccountType } from 'queries/futures/types'; -import { FuturesMarketAsset } from 'sdk/types/futures'; +import { FuturesAccountType, FuturesMarketAsset } from 'sdk/types/futures'; import { EXTERNAL_LINKS } from './links'; diff --git a/containers/Connector/Connector.tsx b/containers/Connector/Connector.tsx index b746363987..7dfd1ab5f8 100644 --- a/containers/Connector/Connector.tsx +++ b/containers/Connector/Connector.tsx @@ -1,14 +1,13 @@ -import { NetworkId, synthetix } from '@synthetixio/contracts-interface'; import { TransactionNotifier as BaseTN } from '@synthetixio/transaction-notifier'; -import { ethers } from 'ethers'; -import { keyBy } from 'lodash'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { createContainer } from 'unstated-next'; import { useAccount, useNetwork, useSigner, useProvider } from 'wagmi'; +import { NetworkId } from 'sdk/types/common'; import { sdk } from 'state/config'; import { useAppDispatch } from 'state/hooks'; -import { resetNetwork, setSigner } from 'state/wallet/actions'; +import { setSigner } from 'state/wallet/actions'; +import { setNetwork } from 'state/wallet/reducer'; import { generateExplorerFunctions, getBaseUrl } from './blockExplorer'; import { activeChainIds, chain, wagmiClient } from './config'; @@ -36,22 +35,9 @@ const useConnector = () => { const l2Provider = useProvider({ chainId: chain.optimism.id }); const { data: signer } = useSigner(); - // Provides a default mainnet provider, irrespective of the current network - const staticMainnetProvider = new ethers.providers.InfuraProvider(); - - const defaultSynthetixjs = useMemo( - () => synthetix({ provider, networkId: network.id as NetworkId }), - [provider, network.id] - ); - - const l2Synthetixjs = useMemo( - () => synthetix({ provider: l2Provider, networkId: chain.optimism.id as NetworkId }), - [l2Provider] - ); - const handleNetworkChange = useCallback( (networkId: NetworkId) => { - dispatch(resetNetwork(networkId)); + dispatch(setNetwork(networkId)); blockExplorer = generateExplorerFunctions(getBaseUrl(networkId)); }, [dispatch] @@ -67,26 +53,10 @@ const useConnector = () => { } }, [provider, handleNetworkChange]); - useEffect(() => { - handleNetworkChange(network.id as NetworkId); - }, [network.id, handleNetworkChange]); - useEffect(() => { dispatch(setSigner(signer)); }, [signer, dispatch]); - const [synthsMap, tokensMap] = useMemo(() => { - if (defaultSynthetixjs == null) return [{}, {}]; - - return [keyBy(defaultSynthetixjs.synths, 'name'), keyBy(defaultSynthetixjs.tokens, 'symbol')]; - }, [defaultSynthetixjs]); - - const l2SynthsMap = useMemo(() => { - if (l2Synthetixjs == null) return {}; - - return keyBy(l2Synthetixjs.synths, 'name'); - }, [l2Synthetixjs]); - return { activeChain, isWalletConnected, @@ -95,11 +65,6 @@ const useConnector = () => { l2Provider, signer, network, - synthsMap, - tokensMap, - staticMainnetProvider, - defaultSynthetixjs, - l2SynthsMap, providerReady, }; }; diff --git a/containers/Connector/blockExplorer.ts b/containers/Connector/blockExplorer.ts index 932638d3d2..ef7c04f3ca 100644 --- a/containers/Connector/blockExplorer.ts +++ b/containers/Connector/blockExplorer.ts @@ -1,10 +1,11 @@ -import { NetworkId, NetworkNameById, NetworkIdByName } from '@synthetixio/contracts-interface'; import { OPTIMISM_NETWORKS } from '@synthetixio/optimism-networks'; +import { NetworkId, NetworkNameById, NetworkIdByName } from 'sdk/types/common'; + export const getBaseUrl = (networkId: NetworkId) => { if (networkId === 10 || networkId === 420) { return OPTIMISM_NETWORKS[networkId as NetworkId]?.blockExplorerUrls[0]; - } else if ((networkId as NetworkId) === NetworkIdByName.mainnet) { + } else if (networkId === NetworkIdByName.mainnet) { return 'https://etherscan.io'; } return `https://${NetworkNameById[networkId]}.etherscan.io`; diff --git a/hooks/useAverageEntryPrice.ts b/hooks/useAverageEntryPrice.ts index 98aece4e5f..3a073cdc73 100644 --- a/hooks/useAverageEntryPrice.ts +++ b/hooks/useAverageEntryPrice.ts @@ -23,7 +23,7 @@ const useAverageEntryPrice = (positionHistory?: FuturesPositionHistory) => { const existingValue = avgEntryPrice.mul(size); const newValue = previewTrade.price.mul(previewTrade.sizeDelta.abs()); const totalValue = existingValue.add(newValue); - return totalValue.div(previewTrade.size.abs()); + return totalValue.div(previewTrade.size.eq(0) ? 1 : previewTrade.size.abs()); } return null; }, [positionHistory, previewTrade]); diff --git a/hooks/useCurrencyPrice.test.tsx b/hooks/useCurrencyPrice.test.tsx deleted file mode 100644 index d8171ab22f..0000000000 --- a/hooks/useCurrencyPrice.test.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import useSynthetixQueries from '@synthetixio/queries'; -import { wei } from '@synthetixio/wei'; -import { renderHook } from '@testing-library/react-hooks'; - -import { SynthetixProvider } from 'testing/unit/mocks/MockProviders'; - -import useCurrencyPrice from './useCurrencyPrice'; - -jest.mock('@synthetixio/queries'); -const useSynthetixQueriesMock = useSynthetixQueries as jest.MockedFunction< - typeof useSynthetixQueries ->; - -describe('useCurrencyPrice', () => { - test('happy path', async () => { - useSynthetixQueriesMock.mockReturnValue({ - useExchangeRatesQuery: jest.fn().mockReturnValue({ - isSuccess: true, - data: { - sBTC: wei(60000), - sUSD: wei(1), - }, - }), - } as any); - expect(1).toBe(1); - - const { result } = renderHook(() => useCurrencyPrice('sBTC'), { wrapper: SynthetixProvider }); - expect(result.current.toString(0)).toBe('60000'); - }); -}); diff --git a/hooks/useCurrencyPrice.ts b/hooks/useCurrencyPrice.ts deleted file mode 100644 index 45a5d935f5..0000000000 --- a/hooks/useCurrencyPrice.ts +++ /dev/null @@ -1,21 +0,0 @@ -import useSynthetixQueries from '@synthetixio/queries'; -import { wei } from '@synthetixio/wei'; - -import { CurrencyKey } from 'constants/currency'; -import { useAppSelector } from 'state/hooks'; -import { selectPreferredCurrency } from 'state/preferences/selectors'; -import { zeroBN } from 'utils/formatters/number'; - -const useCurrencyPrice = (currencyKey: CurrencyKey) => { - const selectedPriceCurrency = useAppSelector(selectPreferredCurrency); - const { useExchangeRatesQuery } = useSynthetixQueries(); - const exchangeRatesQuery = useExchangeRatesQuery(); - const exchangeRates = exchangeRatesQuery.isSuccess ? exchangeRatesQuery.data ?? null : null; - const selectPriceCurrencyRate = exchangeRates && exchangeRates[selectedPriceCurrency.name]; - const currencyUSDPrice = exchangeRates && exchangeRates[currencyKey]; - return !(currencyUSDPrice && selectPriceCurrencyRate) - ? zeroBN - : wei(currencyUSDPrice).div(selectPriceCurrencyRate); -}; - -export default useCurrencyPrice; diff --git a/hooks/useENS.ts b/hooks/useENS.ts index cc20dfdd6e..15b80747b3 100644 --- a/hooks/useENS.ts +++ b/hooks/useENS.ts @@ -1,7 +1,7 @@ import { isAddress } from 'ethers/lib/utils'; import { useEffect, useState } from 'react'; -import Connector from 'containers/Connector'; +import { staticMainnetProvider } from 'utils/network'; type ENSAccount = { ensAddress: string | null; ensName: string | null; ensAvatar: string | null }; @@ -9,7 +9,6 @@ const useENS = (addressOrName: string): ENSAccount => { const [ensAddress, setENSAddress] = useState(null); const [ensName, setENSName] = useState(null); const [ensAvatar, setENSAvatar] = useState(null); - const { staticMainnetProvider } = Connector.useContainer(); useEffect(() => { let mounted = true; @@ -37,7 +36,7 @@ const useENS = (addressOrName: string): ENSAccount => { setENSAvatar(null); setENSName(null); }; - }, [addressOrName, staticMainnetProvider]); + }, [addressOrName]); return { ensAddress, ensName, ensAvatar }; }; diff --git a/hooks/useENSs.ts b/hooks/useENSs.ts index f0568cfa6a..e7cb8978bc 100644 --- a/hooks/useENSs.ts +++ b/hooks/useENSs.ts @@ -3,8 +3,8 @@ import { useQuery, UseQueryOptions } from 'react-query'; import { ENS_REVERSE_LOOKUP } from 'constants/address'; import QUERY_KEYS from 'constants/queryKeys'; -import Connector from 'containers/Connector'; import reverseRecordsAbi from 'sdk/contracts/abis/ReverseRecords.json'; +import { staticMainnetProvider } from 'utils/network'; const ADDRESSES_PER_LOOKUP = 1500; @@ -13,15 +13,12 @@ type EnsInfo = { }; const useENSs = (addresses: string[], options?: UseQueryOptions) => { - const { staticMainnetProvider } = Connector.useContainer(); - return useQuery( QUERY_KEYS.Network.ENSNames(addresses), async () => { const ReverseLookup = new Contract( ENS_REVERSE_LOOKUP, reverseRecordsAbi, - // @ts-ignore provider type staticMainnetProvider ); diff --git a/hooks/useEstimateGasCost.ts b/hooks/useEstimateGasCost.ts deleted file mode 100644 index 236a10e9b2..0000000000 --- a/hooks/useEstimateGasCost.ts +++ /dev/null @@ -1,75 +0,0 @@ -import useSynthetixQueries from '@synthetixio/queries'; -import Wei, { wei } from '@synthetixio/wei'; -import { Contract } from 'ethers'; -import { useMemo, useCallback } from 'react'; - -import { selectGasSpeed } from 'state/app/selectors'; -import { sdk } from 'state/config'; -import { useAppSelector } from 'state/hooks'; -import { newGetExchangeRatesForCurrencies } from 'utils/currencies'; -import { zeroBN } from 'utils/formatters/number'; -import logError from 'utils/logError'; -import { getTransactionPrice } from 'utils/network'; - -export default function useEstimateGasCost() { - const gasSpeed = useAppSelector(selectGasSpeed); - - const { useEthGasPriceQuery, useExchangeRatesQuery } = useSynthetixQueries(); - const ethGasPriceQuery = useEthGasPriceQuery(); - const exchangeRatesQuery = useExchangeRatesQuery(); - - const gasPrice = ethGasPriceQuery.data?.[gasSpeed] ?? null; - - const exchangeRates = useMemo(() => exchangeRatesQuery.data ?? null, [exchangeRatesQuery.data]); - - const ethPriceRate = useMemo( - () => newGetExchangeRatesForCurrencies(exchangeRates, 'sETH', 'sUSD'), - [exchangeRates] - ); - - const estimateSnxTxGasCost = useCallback( - (transaction: any): Wei => { - const gasCost = getTransactionPrice( - gasPrice, - transaction.gasLimit, - ethPriceRate, - transaction.optimismLayerOneFee - ); - return gasCost || zeroBN; - }, - [gasPrice, ethPriceRate] - ); - - const estimateEthersContractTxCost = useCallback( - async ( - contract: Contract, - method: string, - params: any[], - buffer: number = 0 - ): Promise<{ gasPrice: Wei | null; gasLimit: Wei | null }> => { - if (!contract?.estimateGas[method]) throw new Error('Invalid contract method'); - try { - const gasLimit = await contract?.estimateGas[method](...params); - const metaTx = await contract?.populateTransaction[method](...params); - if (!metaTx || !gasLimit || !gasPrice?.gasPrice) return { gasPrice: null, gasLimit: null }; - const gasBuffer = gasLimit.mul(buffer).div(100); - const gasLimitWithBuffer = gasLimit.add(gasBuffer); - const l1Fee = await sdk.transactions.getOptimismLayerOneFees({ - ...metaTx, - gasPrice: gasPrice?.gasPrice?.toNumber(), - gasLimit: Number(gasLimitWithBuffer), - }); - return { - gasPrice: getTransactionPrice(gasPrice, gasLimit, ethPriceRate, l1Fee) || zeroBN, - gasLimit: wei(gasLimitWithBuffer, 0, true), - }; - } catch (err) { - logError(err); - return { gasPrice: null, gasLimit: null }; - } - }, - [gasPrice, ethPriceRate] - ); - - return { estimateSnxTxGasCost, estimateEthersContractTxCost }; -} diff --git a/hooks/useFuturesMarketClosed.ts b/hooks/useFuturesMarketClosed.ts deleted file mode 100644 index 06705e496f..0000000000 --- a/hooks/useFuturesMarketClosed.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { SynthSuspensionReason } from '@synthetixio/queries'; - -import useFuturesSuspensionQuery from 'queries/futures/useFuturesSuspensionQuery'; -import { FuturesMarketKey } from 'sdk/types/futures'; - -export type FuturesClosureReason = SynthSuspensionReason; -export type MarketClosure = ReturnType; - -const useFuturesMarketClosed = (marketKey: FuturesMarketKey | null) => { - const futuresMarketSuspendedQuery = useFuturesSuspensionQuery(marketKey); - - const isFuturesMarketClosed = - futuresMarketSuspendedQuery.isSuccess && futuresMarketSuspendedQuery.data - ? futuresMarketSuspendedQuery.data.isFuturesMarketClosed - : null; - - const reason = - futuresMarketSuspendedQuery.isSuccess && futuresMarketSuspendedQuery.data - ? futuresMarketSuspendedQuery.data.futuresClosureReason - : null; - - return { - isFuturesMarketClosed, - futuresClosureReason: reason as FuturesClosureReason, - }; -}; - -export default useFuturesMarketClosed; diff --git a/hooks/useGas.ts b/hooks/useGas.ts deleted file mode 100644 index 5db403dbbc..0000000000 --- a/hooks/useGas.ts +++ /dev/null @@ -1,101 +0,0 @@ -import useSynthetixQueries, { GasPrice } from '@synthetixio/queries'; -import { wei } from '@synthetixio/wei'; -import { BigNumber } from 'ethers'; -import { useCallback, useEffect, useMemo, useState } from 'react'; - -import { selectGasPrice, selectGasSpeed } from 'state/app/selectors'; -import { useAppSelector } from 'state/hooks'; -import { gasPriceInWei, normalizeGasLimit } from 'utils/network'; - -import useIsL1 from './useIsL1'; - -export const parseGasPriceObject = (gasPriceObject: GasPrice) => { - const { gasPrice, maxFeePerGas } = gasPriceObject; - if (gasPrice) { - return wei(gasPriceObject.gasPrice, 9).toNumber(); - } else if (maxFeePerGas) { - return wei(gasPriceObject.maxFeePerGas, 9).toNumber(); - } else { - return null; - } -}; - -type GasConfigL1 = { - maxPriorityFeePerGas?: BigNumber; - maxFeePerGas?: BigNumber; -}; - -type GasConfigL2 = { - gasPrice?: BigNumber; -}; - -type GasConfig = GasConfigL1 | GasConfigL2; - -const useGas = () => { - const isMainnet = useIsL1(); - const { useEthGasPriceQuery } = useSynthetixQueries(); - const ethGasPriceQuery = useEthGasPriceQuery(); - const gasSpeed = useAppSelector(selectGasSpeed); - const customGasPrice = useAppSelector(selectGasPrice); - - const [gasConfig, setGasConfig] = useState({} as GasConfig); - const gasPrices = useMemo(() => ethGasPriceQuery?.data ?? undefined, [ethGasPriceQuery.data]); - const isCustomGasPrice: boolean = useMemo(() => customGasPrice !== '', [customGasPrice]); - - const selectedGas: GasPrice = useMemo(() => gasPrices?.[gasSpeed] ?? {}, [gasPrices, gasSpeed]); - - const gasPrice = useMemo(() => { - return isCustomGasPrice - ? Number(customGasPrice) - : gasPrices != null - ? parseGasPriceObject(gasPrices[gasSpeed]) - : null; - }, [customGasPrice, isCustomGasPrice, gasPrices, gasSpeed]); - - const gasPriceWei = useMemo(() => { - return !gasPrice ? 0 : gasPriceInWei(gasPrice); - }, [gasPrice]); - - const getGasLimitEstimate = useCallback(async (getGasEstimate: () => Promise): Promise< - number | null - > => { - try { - const gasEstimate = await getGasEstimate(); - return normalizeGasLimit(Number(gasEstimate)); - } catch (e) { - return null; - } - }, []); - - useEffect(() => { - const maxPriorityFeePerGas = selectedGas.maxPriorityFeePerGas; - const maxFeePerGasValue = isCustomGasPrice ? gasPriceWei : selectedGas.maxFeePerGas; - - const l1GasConfig = { maxPriorityFeePerGas, maxFeePerGas: maxFeePerGasValue }; - const l2GasConfig = { gasPrice: gasPriceWei }; - - const config = isMainnet ? l1GasConfig : l2GasConfig; - - setGasConfig(config as GasPrice); - }, [ - gasPriceWei, - isCustomGasPrice, - isMainnet, - selectedGas.baseFeePerGas, - selectedGas.maxFeePerGas, - selectedGas.maxPriorityFeePerGas, - ]); - - return { - gasPrice, - gasPriceWei, - getGasLimitEstimate, - gasPrices, - gasSpeed, - isCustomGasPrice, - customGasPrice, - gasConfig, - }; -}; - -export default useGas; diff --git a/hooks/useMarketClosed.ts b/hooks/useMarketClosed.ts deleted file mode 100644 index 8a8037e23f..0000000000 --- a/hooks/useMarketClosed.ts +++ /dev/null @@ -1,25 +0,0 @@ -import useSynthetixQueries from '@synthetixio/queries'; -import { SynthSuspensionReason } from '@synthetixio/queries'; -import { useMemo } from 'react'; - -import { CurrencyKey } from 'constants/currency'; - -export type MarketClosureReason = 'frozen' | SynthSuspensionReason; -export type MarketClosure = ReturnType; - -const useMarketClosed = (currencyKey: CurrencyKey | null) => { - const { useSynthSuspensionQuery } = useSynthetixQueries(); - const currencySuspendedQuery = useSynthSuspensionQuery(currencyKey); - - const { isMarketClosed, marketClosureReason } = useMemo( - () => ({ - isMarketClosed: currencySuspendedQuery.data?.isSuspended ?? false, - marketClosureReason: currencySuspendedQuery.data?.reason as MarketClosureReason, - }), - [currencySuspendedQuery?.data] - ); - - return { isMarketClosed, marketClosureReason }; -}; - -export default useMarketClosed; diff --git a/hooks/useNetworkSwitcher.ts b/hooks/useNetworkSwitcher.ts index f1681f852a..373c165702 100644 --- a/hooks/useNetworkSwitcher.ts +++ b/hooks/useNetworkSwitcher.ts @@ -1,55 +1,31 @@ -import { addOptimismNetworkToMetamask } from '@synthetixio/optimism-networks'; -import { L2_TO_L1_NETWORK_MAPPER } from '@synthetixio/optimism-networks'; -import { utils, BigNumber } from 'ethers'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useConnect } from 'wagmi'; +import { useConnect, useSwitchNetwork } from 'wagmi'; import Connector from 'containers/Connector'; -import { chain } from 'containers/Connector/config'; const useNetworkSwitcher = () => { - const { network, isWalletConnected } = Connector.useContainer(); + const { isWalletConnected } = Connector.useContainer(); const { connect: connectWallet } = useConnect(); const [, setNetworkError] = useState(null); + const { switchNetwork } = useSwitchNetwork(); const { t } = useTranslation(); - const switchToL1 = async () => { - if (!isWalletConnected) connectWallet(); - try { - if (!window.ethereum || !window.ethereum.isMetaMask) { - return setNetworkError(t('user-menu.error.please-install-metamask')); - } - setNetworkError(null); - - const formattedChainId = utils.hexStripZeros( - BigNumber.from(L2_TO_L1_NETWORK_MAPPER[network?.id ?? chain.optimism.id]).toHexString() - ); - (window.ethereum as any).request({ - method: 'wallet_switchEthereumChain', - params: [{ chainId: formattedChainId }], - }); - } catch (e) { - setNetworkError(e.message); - } - }; - const switchToL2 = async () => { if (!isWalletConnected) connectWallet(); try { - if (!window.ethereum || !window.ethereum.isMetaMask) { + if (!switchNetwork) { return setNetworkError(t('user-menu.error.please-install-metamask')); } setNetworkError(null); - addOptimismNetworkToMetamask({ ethereum: window.ethereum }); + switchNetwork(10); } catch (e) { setNetworkError(e.message); } }; return { - switchToL1, switchToL2, }; }; diff --git a/hooks/useSUSDContract.ts b/hooks/useSUSDContract.ts deleted file mode 100644 index 0be03cd065..0000000000 --- a/hooks/useSUSDContract.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { useMemo } from 'react'; - -import Connector from 'containers/Connector'; -import { ERC20 } from 'sdk/contracts/types/ERC20'; -import { ERC20__factory } from 'sdk/contracts/types/factories/ERC20__factory'; - -export default function useSUSDContract(): ERC20 | null { - const { tokensMap: synthTokensMap, signer } = Connector.useContainer(); - - const susdContract = useMemo(() => { - if (!signer || !synthTokensMap.sUSD) return null; - - return ERC20__factory.connect(synthTokensMap.sUSD.address, signer); - }, [synthTokensMap.sUSD, signer]); - - return susdContract; -} diff --git a/package-lock.json b/package-lock.json index 92cb1ab99d..24cc446cb4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "kwenta", - "version": "6.2.2", + "version": "6.2.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "kwenta", - "version": "6.2.2", + "version": "6.2.3", "hasInstallScript": true, "dependencies": { "@artsy/fresnel": "1.7.0", @@ -23,10 +23,8 @@ "@sentry/browser": "^7.37.0", "@sentry/tracing": "^7.37.0", "@socket.tech/plugin": "^1.0.3", - "@synthetixio/contracts-interface": "2.76.3", "@synthetixio/optimism-networks": "2.74.6", "@synthetixio/providers": "2.74.10", - "@synthetixio/queries": "3.0.11", "@synthetixio/transaction-notifier": "2.74.12", "@synthetixio/wei": "2.74.4", "axios": "0.27.2", @@ -2880,11 +2878,6 @@ "js-sha3": "^0.8.0" } }, - "node_modules/@ensdomains/eth-ens-namehash": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@ensdomains/eth-ens-namehash/-/eth-ens-namehash-2.0.15.tgz", - "integrity": "sha512-JRDFP6+Hczb1E0/HhIg0PONgBYasfGfDheujmfxaZaAv/NAH4jE6Kf48WbqfRZdxt4IZI3jl3Ri7sZ1nP09lgw==" - }, "node_modules/@ensdomains/resolver": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/@ensdomains/resolver/-/resolver-0.2.4.tgz", @@ -7101,48 +7094,6 @@ "@sinonjs/commons": "^1.7.0" } }, - "node_modules/@snapshot-labs/snapshot.js": { - "version": "0.4.40", - "resolved": "https://registry.npmjs.org/@snapshot-labs/snapshot.js/-/snapshot.js-0.4.40.tgz", - "integrity": "sha512-/6VHbDZANx36c4nMCiPrg3LR52jjysow+F/79lp7h8B3sNHMqHCTDmheBM6pTguQ3+cCgfKLOrZn62C9laQW2A==", - "dependencies": { - "@ensdomains/eth-ens-namehash": "^2.0.15", - "@ethersproject/abi": "^5.6.4", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/contracts": "^5.6.2", - "@ethersproject/hash": "^5.6.1", - "@ethersproject/providers": "^5.6.8", - "@ethersproject/wallet": "^5.6.2", - "ajv": "^8.11.0", - "ajv-formats": "^2.1.1", - "cross-fetch": "^3.1.5", - "json-to-graphql-query": "^2.2.4", - "lodash.set": "^4.3.2" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@snapshot-labs/snapshot.js/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@snapshot-labs/snapshot.js/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, "node_modules/@socket.tech/ll-core": { "version": "0.1.26", "resolved": "https://registry.npmjs.org/@socket.tech/ll-core/-/ll-core-0.1.26.tgz", @@ -19195,15 +19146,6 @@ "synthetix": "2.76.1" } }, - "node_modules/@synthetixio/generate-subgraph-query": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@synthetixio/generate-subgraph-query/-/generate-subgraph-query-1.0.2.tgz", - "integrity": "sha512-4H/Xr6KxfU/8Bw0lZ8X73AwGPhVU/TzlURMV1AnJDLT1Duf0BGr9z75fkdYpn1PffAp9/wtFntoEWl5urbE1ig==", - "dependencies": { - "@synthetixio/wei": "2.74.4", - "lodash": "^4.17.21" - } - }, "node_modules/@synthetixio/js": { "version": "2.41.0", "resolved": "https://registry.npmjs.org/@synthetixio/js/-/js-2.41.0.tgz", @@ -19415,56 +19357,6 @@ "@metamask/providers": "^8.1.1" } }, - "node_modules/@synthetixio/queries": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@synthetixio/queries/-/queries-3.0.11.tgz", - "integrity": "sha512-Pr5UDo6G6Hwx0TcSlrG7VmlsGP6Clj9NUJ3kv7e6DNJJj0smHv6hqgwF5WZ69XNJoxxQMvpP32p4q2cZI+4yjw==", - "dependencies": { - "@ethersproject/abstract-signer": "^5.6.2", - "@ethersproject/address": "^5.6.1", - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/constants": "^5.6.1", - "@ethersproject/contracts": "^5.6.2", - "@ethersproject/providers": "^5.6.8", - "@ethersproject/strings": "^5.6.1", - "@ethersproject/transactions": "^5.6.2", - "@ethersproject/units": "^5.6.1", - "@snapshot-labs/snapshot.js": "^0.4.13", - "@synthetixio/contracts": "1.1.5", - "@synthetixio/contracts-interface": "2.76.3", - "@synthetixio/generate-subgraph-query": "1.0.2", - "@synthetixio/optimism-networks": "2.74.5", - "@synthetixio/wei": "2.74.4", - "axios": "^0.21.4", - "date-fns": "^2.19.0", - "graphql": "^15.5.0", - "graphql-request": "^3.4.0", - "lodash": "^4.17.21" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*", - "react-query": "3.16.x" - } - }, - "node_modules/@synthetixio/queries/node_modules/@synthetixio/optimism-networks": { - "version": "2.74.5", - "resolved": "https://registry.npmjs.org/@synthetixio/optimism-networks/-/optimism-networks-2.74.5.tgz", - "integrity": "sha512-g6Ndi5MWy0qHYlgK7VWavxEah0YwP6NJVBA2kKQXBuGjFlvo3u8NYk93vWwd/u9UaL6jdVcNz23staQkTTDjiQ==", - "dependencies": { - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@metamask/providers": "^8.1.1" - } - }, - "node_modules/@synthetixio/queries/node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "dependencies": { - "follow-redirects": "^1.14.0" - } - }, "node_modules/@synthetixio/synpress": { "version": "1.2.0", "resolved": "git+ssh://git@github.com/avclarke/synpress.git#19b9c0b196124303a554e8cc64c55291e7745395", @@ -24098,42 +23990,6 @@ "ajv": ">=5.0.0" } }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, "node_modules/ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -43336,11 +43192,6 @@ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" }, - "node_modules/json-to-graphql-query": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/json-to-graphql-query/-/json-to-graphql-query-2.2.4.tgz", - "integrity": "sha512-vNvsOKDSlEqYCzejI1xHS9Hm738dSnG4Upy09LUGqyybZXSIIb7NydDphB/6WxW2EEVpPU4JeU/Yo63Nw9dEJg==" - }, "node_modules/json2mq": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", @@ -44435,11 +44286,6 @@ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "dev": true }, - "node_modules/lodash.set": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg==" - }, "node_modules/lodash.snakecase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", @@ -58447,9 +58293,9 @@ } }, "node_modules/webpack": { - "version": "5.75.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz", - "integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==", + "version": "5.76.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", + "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", @@ -61300,11 +61146,6 @@ "js-sha3": "^0.8.0" } }, - "@ensdomains/eth-ens-namehash": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@ensdomains/eth-ens-namehash/-/eth-ens-namehash-2.0.15.tgz", - "integrity": "sha512-JRDFP6+Hczb1E0/HhIg0PONgBYasfGfDheujmfxaZaAv/NAH4jE6Kf48WbqfRZdxt4IZI3jl3Ri7sZ1nP09lgw==" - }, "@ensdomains/resolver": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/@ensdomains/resolver/-/resolver-0.2.4.tgz", @@ -64385,43 +64226,6 @@ "@sinonjs/commons": "^1.7.0" } }, - "@snapshot-labs/snapshot.js": { - "version": "0.4.40", - "resolved": "https://registry.npmjs.org/@snapshot-labs/snapshot.js/-/snapshot.js-0.4.40.tgz", - "integrity": "sha512-/6VHbDZANx36c4nMCiPrg3LR52jjysow+F/79lp7h8B3sNHMqHCTDmheBM6pTguQ3+cCgfKLOrZn62C9laQW2A==", - "requires": { - "@ensdomains/eth-ens-namehash": "^2.0.15", - "@ethersproject/abi": "^5.6.4", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/contracts": "^5.6.2", - "@ethersproject/hash": "^5.6.1", - "@ethersproject/providers": "^5.6.8", - "@ethersproject/wallet": "^5.6.2", - "ajv": "^8.11.0", - "ajv-formats": "^2.1.1", - "cross-fetch": "^3.1.5", - "json-to-graphql-query": "^2.2.4", - "lodash.set": "^4.3.2" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - } - } - }, "@socket.tech/ll-core": { "version": "0.1.26", "resolved": "https://registry.npmjs.org/@socket.tech/ll-core/-/ll-core-0.1.26.tgz", @@ -73557,15 +73361,6 @@ "synthetix": "2.76.1" } }, - "@synthetixio/generate-subgraph-query": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@synthetixio/generate-subgraph-query/-/generate-subgraph-query-1.0.2.tgz", - "integrity": "sha512-4H/Xr6KxfU/8Bw0lZ8X73AwGPhVU/TzlURMV1AnJDLT1Duf0BGr9z75fkdYpn1PffAp9/wtFntoEWl5urbE1ig==", - "requires": { - "@synthetixio/wei": "2.74.4", - "lodash": "^4.17.21" - } - }, "@synthetixio/js": { "version": "2.41.0", "resolved": "https://registry.npmjs.org/@synthetixio/js/-/js-2.41.0.tgz", @@ -73773,53 +73568,6 @@ } } }, - "@synthetixio/queries": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@synthetixio/queries/-/queries-3.0.11.tgz", - "integrity": "sha512-Pr5UDo6G6Hwx0TcSlrG7VmlsGP6Clj9NUJ3kv7e6DNJJj0smHv6hqgwF5WZ69XNJoxxQMvpP32p4q2cZI+4yjw==", - "requires": { - "@ethersproject/abstract-signer": "^5.6.2", - "@ethersproject/address": "^5.6.1", - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/constants": "^5.6.1", - "@ethersproject/contracts": "^5.6.2", - "@ethersproject/providers": "^5.6.8", - "@ethersproject/strings": "^5.6.1", - "@ethersproject/transactions": "^5.6.2", - "@ethersproject/units": "^5.6.1", - "@snapshot-labs/snapshot.js": "^0.4.13", - "@synthetixio/contracts": "1.1.5", - "@synthetixio/contracts-interface": "2.76.3", - "@synthetixio/generate-subgraph-query": "1.0.2", - "@synthetixio/optimism-networks": "2.74.5", - "@synthetixio/wei": "2.74.4", - "axios": "^0.21.4", - "date-fns": "^2.19.0", - "graphql": "^15.5.0", - "graphql-request": "^3.4.0", - "lodash": "^4.17.21" - }, - "dependencies": { - "@synthetixio/optimism-networks": { - "version": "2.74.5", - "resolved": "https://registry.npmjs.org/@synthetixio/optimism-networks/-/optimism-networks-2.74.5.tgz", - "integrity": "sha512-g6Ndi5MWy0qHYlgK7VWavxEah0YwP6NJVBA2kKQXBuGjFlvo3u8NYk93vWwd/u9UaL6jdVcNz23staQkTTDjiQ==", - "requires": { - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@metamask/providers": "^8.1.1" - } - }, - "axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "requires": { - "follow-redirects": "^1.14.0" - } - } - } - }, "@synthetixio/synpress": { "version": "git+ssh://git@github.com/avclarke/synpress.git#19b9c0b196124303a554e8cc64c55291e7745395", "dev": true, @@ -77613,32 +77361,6 @@ "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==" }, - "ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "requires": { - "ajv": "^8.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - } - } - }, "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -92743,11 +92465,6 @@ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" }, - "json-to-graphql-query": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/json-to-graphql-query/-/json-to-graphql-query-2.2.4.tgz", - "integrity": "sha512-vNvsOKDSlEqYCzejI1xHS9Hm738dSnG4Upy09LUGqyybZXSIIb7NydDphB/6WxW2EEVpPU4JeU/Yo63Nw9dEJg==" - }, "json2mq": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", @@ -93618,11 +93335,6 @@ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "dev": true }, - "lodash.set": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg==" - }, "lodash.snakecase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", @@ -104624,9 +104336,9 @@ } }, "webpack": { - "version": "5.75.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz", - "integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==", + "version": "5.76.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", + "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.3", diff --git a/package.json b/package.json index 5c72f0ce1f..74bcfb05ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kwenta", - "version": "6.2.2", + "version": "6.2.3", "scripts": { "dev": "next", "build": "next build", @@ -39,10 +39,8 @@ "@socket.tech/plugin": "^1.0.3", "@sentry/browser": "^7.37.0", "@sentry/tracing": "^7.37.0", - "@synthetixio/contracts-interface": "2.76.3", "@synthetixio/optimism-networks": "2.74.6", "@synthetixio/providers": "2.74.10", - "@synthetixio/queries": "3.0.11", "@synthetixio/transaction-notifier": "2.74.12", "@synthetixio/wei": "2.74.4", "axios": "0.27.2", diff --git a/pages/_app.tsx b/pages/_app.tsx index 4b3c898a71..0ebb641c94 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -2,8 +2,6 @@ import { createTheme, MuiThemeProvider } from '@material-ui/core'; import { darkTheme, lightTheme, RainbowKitProvider } from '@rainbow-me/rainbowkit'; import * as Sentry from '@sentry/browser'; import { BrowserTracing } from '@sentry/tracing'; -import { NetworkId } from '@synthetixio/contracts-interface'; -import { createQueryContext, SynthetixQueryContextProvider } from '@synthetixio/queries'; import { NextPage } from 'next'; import { AppProps } from 'next/app'; import Head from 'next/head'; @@ -17,7 +15,7 @@ import { WagmiConfig } from 'wagmi'; import ErrorNotifier from 'components/ErrorView/ErrorNotifier'; import Connector from 'containers/Connector'; -import { chains, wagmiClient, chain } from 'containers/Connector/config'; +import { chains, wagmiClient } from 'containers/Connector/config'; import useMonitorTransactions from 'hooks/useMonitorTransactions'; import AcknowledgementModal from 'sections/app/AcknowledgementModal'; import Layout from 'sections/shared/Layout'; @@ -46,7 +44,6 @@ type AppPropsWithLayout = AppProps & { Component: NextPageWithLayout; }; -process.env.GIT_HASH_ID!.toString(); Sentry.init({ dsn: 'https://d48644bc80d04977a26132b346417210@o4504363236851712.ingest.sentry.io/4504363261362177', @@ -61,14 +58,7 @@ Sentry.init({ }); const InnerApp: FC = ({ Component, pageProps }: AppPropsWithLayout) => { - const { - signer, - provider, - l2Provider, - network, - providerReady, - defaultSynthetixjs: synthetixjs, - } = Connector.useContainer(); + const { providerReady } = Connector.useContainer(); useAppData(providerReady); useMonitorTransactions(); @@ -89,29 +79,12 @@ const InnerApp: FC = ({ Component, pageProps }: AppPropsWithLayout) => - - - - {getLayout()} - - - - + + + {getLayout()} + + + diff --git a/pages/_document.tsx b/pages/_document.tsx index 0856b165e3..8c7f6395d8 100644 --- a/pages/_document.tsx +++ b/pages/_document.tsx @@ -84,6 +84,10 @@ export default class MyDocument extends Document { type="font/woff2" crossOrigin="anonymous" /> +
diff --git a/pages/dashboard/markets.tsx b/pages/dashboard/markets.tsx index 8b77328469..31b7d0f5b2 100644 --- a/pages/dashboard/markets.tsx +++ b/pages/dashboard/markets.tsx @@ -1,19 +1,19 @@ import Head from 'next/head'; import { useTranslation } from 'react-i18next'; -import Connector from 'containers/Connector'; import DashboardLayout from 'sections/dashboard/DashboardLayout'; import Markets from 'sections/dashboard/Markets'; import GitHashID from 'sections/shared/Layout/AppLayout/GitHashID'; import { fetchMarkets } from 'state/futures/actions'; -import { usePollAction } from 'state/hooks'; +import { useAppSelector, usePollAction } from 'state/hooks'; +import { selectNetwork } from 'state/wallet/selectors'; type MarketsProps = React.FC & { getLayout: (page: HTMLElement) => JSX.Element }; const MarketsPage: MarketsProps = () => { const { t } = useTranslation(); - const { network } = Connector.useContainer(); - usePollAction('fetchMarkets', fetchMarkets, { dependencies: [network.id] }); + const network = useAppSelector(selectNetwork); + usePollAction('fetchMarkets', fetchMarkets, { dependencies: [network] }); return ( <> diff --git a/queries/coingecko/useCoinGeckoCoinListQuery.ts b/queries/coingecko/useCoinGeckoCoinListQuery.ts deleted file mode 100644 index 0f01d06c75..0000000000 --- a/queries/coingecko/useCoinGeckoCoinListQuery.ts +++ /dev/null @@ -1,34 +0,0 @@ -import axios from 'axios'; -import keyBy from 'lodash/keyBy'; -import { UseQueryOptions, useQuery } from 'react-query'; - -import QUERY_KEYS from 'constants/queryKeys'; - -import { CG_BASE_API_URL } from './constants'; - -type CoinListItem = { - id: string; - symbol: string; - name: string; -}; - -type CoinListMap = Record; - -const useCoinGeckoCoinListQuery = (options?: UseQueryOptions) => { - return useQuery( - QUERY_KEYS.CoinGecko.CoinList, - async () => { - const response = await axios.get(`${CG_BASE_API_URL}/coins/list`); - - return keyBy(response.data, (item) => item.symbol.toLowerCase()); - }, - { - refetchInterval: false, - refetchOnWindowFocus: false, - refetchOnMount: false, - ...options, - } - ); -}; - -export default useCoinGeckoCoinListQuery; diff --git a/queries/futures/subgraph.ts b/queries/futures/subgraph.ts index 43d5e3e434..10da271d7a 100644 --- a/queries/futures/subgraph.ts +++ b/queries/futures/subgraph.ts @@ -25,262 +25,6 @@ export type MultiQueryOptions = { orderDirection?: 'asc' | 'desc'; }; const MAX_PAGE = 1000; -export type AtomicSynthExchangeFilter = { - id?: string | null; - id_not?: string | null; - id_gt?: string | null; - id_lt?: string | null; - id_gte?: string | null; - id_lte?: string | null; - id_in?: string[]; - id_not_in?: string[]; - account?: string | null; - account_not?: string | null; - account_gt?: string | null; - account_lt?: string | null; - account_gte?: string | null; - account_lte?: string | null; - account_in?: string[]; - account_not_in?: string[]; - account_contains?: string | null; - account_contains_nocase?: string | null; - account_not_contains?: string | null; - account_not_contains_nocase?: string | null; - account_starts_with?: string | null; - account_starts_with_nocase?: string | null; - account_not_starts_with?: string | null; - account_not_starts_with_nocase?: string | null; - account_ends_with?: string | null; - account_ends_with_nocase?: string | null; - account_not_ends_with?: string | null; - account_not_ends_with_nocase?: string | null; - account_?: ExchangerFilter | null; - fromSynth?: string | null; - fromSynth_not?: string | null; - fromSynth_gt?: string | null; - fromSynth_lt?: string | null; - fromSynth_gte?: string | null; - fromSynth_lte?: string | null; - fromSynth_in?: string[]; - fromSynth_not_in?: string[]; - fromSynth_contains?: string | null; - fromSynth_contains_nocase?: string | null; - fromSynth_not_contains?: string | null; - fromSynth_not_contains_nocase?: string | null; - fromSynth_starts_with?: string | null; - fromSynth_starts_with_nocase?: string | null; - fromSynth_not_starts_with?: string | null; - fromSynth_not_starts_with_nocase?: string | null; - fromSynth_ends_with?: string | null; - fromSynth_ends_with_nocase?: string | null; - fromSynth_not_ends_with?: string | null; - fromSynth_not_ends_with_nocase?: string | null; - fromSynth_?: SynthFilter | null; - toSynth?: string | null; - toSynth_not?: string | null; - toSynth_gt?: string | null; - toSynth_lt?: string | null; - toSynth_gte?: string | null; - toSynth_lte?: string | null; - toSynth_in?: string[]; - toSynth_not_in?: string[]; - toSynth_contains?: string | null; - toSynth_contains_nocase?: string | null; - toSynth_not_contains?: string | null; - toSynth_not_contains_nocase?: string | null; - toSynth_starts_with?: string | null; - toSynth_starts_with_nocase?: string | null; - toSynth_not_starts_with?: string | null; - toSynth_not_starts_with_nocase?: string | null; - toSynth_ends_with?: string | null; - toSynth_ends_with_nocase?: string | null; - toSynth_not_ends_with?: string | null; - toSynth_not_ends_with_nocase?: string | null; - toSynth_?: SynthFilter | null; - fromAmount?: WeiSource | null; - fromAmount_not?: WeiSource | null; - fromAmount_gt?: WeiSource | null; - fromAmount_lt?: WeiSource | null; - fromAmount_gte?: WeiSource | null; - fromAmount_lte?: WeiSource | null; - fromAmount_in?: WeiSource[]; - fromAmount_not_in?: WeiSource[]; - fromAmountInUSD?: WeiSource | null; - fromAmountInUSD_not?: WeiSource | null; - fromAmountInUSD_gt?: WeiSource | null; - fromAmountInUSD_lt?: WeiSource | null; - fromAmountInUSD_gte?: WeiSource | null; - fromAmountInUSD_lte?: WeiSource | null; - fromAmountInUSD_in?: WeiSource[]; - fromAmountInUSD_not_in?: WeiSource[]; - toAmount?: WeiSource | null; - toAmount_not?: WeiSource | null; - toAmount_gt?: WeiSource | null; - toAmount_lt?: WeiSource | null; - toAmount_gte?: WeiSource | null; - toAmount_lte?: WeiSource | null; - toAmount_in?: WeiSource[]; - toAmount_not_in?: WeiSource[]; - toAmountInUSD?: WeiSource | null; - toAmountInUSD_not?: WeiSource | null; - toAmountInUSD_gt?: WeiSource | null; - toAmountInUSD_lt?: WeiSource | null; - toAmountInUSD_gte?: WeiSource | null; - toAmountInUSD_lte?: WeiSource | null; - toAmountInUSD_in?: WeiSource[]; - toAmountInUSD_not_in?: WeiSource[]; - feesInUSD?: WeiSource | null; - feesInUSD_not?: WeiSource | null; - feesInUSD_gt?: WeiSource | null; - feesInUSD_lt?: WeiSource | null; - feesInUSD_gte?: WeiSource | null; - feesInUSD_lte?: WeiSource | null; - feesInUSD_in?: WeiSource[]; - feesInUSD_not_in?: WeiSource[]; - toAddress?: string | null; - toAddress_not?: string | null; - toAddress_in?: string[]; - toAddress_not_in?: string[]; - toAddress_contains?: string | null; - toAddress_not_contains?: string | null; - timestamp?: WeiSource | null; - timestamp_not?: WeiSource | null; - timestamp_gt?: WeiSource | null; - timestamp_lt?: WeiSource | null; - timestamp_gte?: WeiSource | null; - timestamp_lte?: WeiSource | null; - timestamp_in?: WeiSource[]; - timestamp_not_in?: WeiSource[]; - gasPrice?: WeiSource | null; - gasPrice_not?: WeiSource | null; - gasPrice_gt?: WeiSource | null; - gasPrice_lt?: WeiSource | null; - gasPrice_gte?: WeiSource | null; - gasPrice_lte?: WeiSource | null; - gasPrice_in?: WeiSource[]; - gasPrice_not_in?: WeiSource[]; - _change_block?: any | null; -}; -export type AtomicSynthExchangeResult = { - id: string; - account: Partial; - fromSynth: Partial | null; - toSynth: Partial | null; - fromAmount: Wei; - fromAmountInUSD: Wei; - toAmount: Wei; - toAmountInUSD: Wei; - feesInUSD: Wei; - toAddress: string; - timestamp: Wei; - gasPrice: Wei; -}; -export type AtomicSynthExchangeFields = { - id: true; - account: ExchangerFields; - fromSynth: SynthFields; - toSynth: SynthFields; - fromAmount: true; - fromAmountInUSD: true; - toAmount: true; - toAmountInUSD: true; - feesInUSD: true; - toAddress: true; - timestamp: true; - gasPrice: true; -}; -export type AtomicSynthExchangeArgs = { - [Property in keyof Pick]: AtomicSynthExchangeFields[Property]; -}; -export const getAtomicSynthExchangeById = async function < - K extends keyof AtomicSynthExchangeResult ->( - url: string, - options: SingleQueryOptions, - args: AtomicSynthExchangeArgs -): Promise> { - const res = await axios.post(url, { - query: generateGql('atomicSynthExchange', options, args), - }); - const r = res.data as any; - if (r.errors && r.errors.length) { - throw new Error(r.errors[0].message); - } - const obj = r.data[Object.keys(r.data)[0]] as any; - const formattedObj: any = {}; - if (obj['id']) formattedObj['id'] = obj['id']; - if (obj['account']) formattedObj['account'] = obj['account']; - if (obj['fromSynth']) formattedObj['fromSynth'] = obj['fromSynth']; - if (obj['toSynth']) formattedObj['toSynth'] = obj['toSynth']; - if (obj['fromAmount']) formattedObj['fromAmount'] = wei(obj['fromAmount']); - if (obj['fromAmountInUSD']) formattedObj['fromAmountInUSD'] = wei(obj['fromAmountInUSD']); - if (obj['toAmount']) formattedObj['toAmount'] = wei(obj['toAmount']); - if (obj['toAmountInUSD']) formattedObj['toAmountInUSD'] = wei(obj['toAmountInUSD']); - if (obj['feesInUSD']) formattedObj['feesInUSD'] = wei(obj['feesInUSD']); - if (obj['toAddress']) formattedObj['toAddress'] = obj['toAddress']; - if (obj['timestamp']) formattedObj['timestamp'] = wei(obj['timestamp'], 0); - if (obj['gasPrice']) formattedObj['gasPrice'] = wei(obj['gasPrice'], 0); - return formattedObj as Pick; -}; -export const getAtomicSynthExchanges = async function ( - url: string, - options: MultiQueryOptions, - args: AtomicSynthExchangeArgs -): Promise[]> { - const paginatedOptions: Partial> = { ...options }; - let paginationKey: keyof AtomicSynthExchangeFilter | null = null; - let paginationValue = ''; - if (options.first && options.first > MAX_PAGE) { - paginatedOptions.first = MAX_PAGE; - paginatedOptions.orderBy = options.orderBy || 'id'; - paginatedOptions.orderDirection = options.orderDirection || 'asc'; - paginationKey = (paginatedOptions.orderBy + - (paginatedOptions.orderDirection === 'asc' - ? '_gt' - : '_lt')) as keyof AtomicSynthExchangeFilter; - paginatedOptions.where = { ...options.where }; - } - let results: Pick[] = []; - do { - if (paginationKey && paginationValue) - paginatedOptions.where![paginationKey] = paginationValue as any; - const res = await axios.post(url, { - query: generateGql('atomicSynthExchanges', paginatedOptions, args), - }); - const r = res.data as any; - if (r.errors && r.errors.length) { - throw new Error(r.errors[0].message); - } - const rawResults = r.data[Object.keys(r.data)[0]] as any[]; - const newResults = rawResults.map((obj) => { - const formattedObj: any = {}; - if (obj['id']) formattedObj['id'] = obj['id']; - if (obj['account']) formattedObj['account'] = obj['account']; - if (obj['fromSynth']) formattedObj['fromSynth'] = obj['fromSynth']; - if (obj['toSynth']) formattedObj['toSynth'] = obj['toSynth']; - if (obj['fromAmount']) formattedObj['fromAmount'] = wei(obj['fromAmount']); - if (obj['fromAmountInUSD']) formattedObj['fromAmountInUSD'] = wei(obj['fromAmountInUSD']); - if (obj['toAmount']) formattedObj['toAmount'] = wei(obj['toAmount']); - if (obj['toAmountInUSD']) formattedObj['toAmountInUSD'] = wei(obj['toAmountInUSD']); - if (obj['feesInUSD']) formattedObj['feesInUSD'] = wei(obj['feesInUSD']); - if (obj['toAddress']) formattedObj['toAddress'] = obj['toAddress']; - if (obj['timestamp']) formattedObj['timestamp'] = wei(obj['timestamp'], 0); - if (obj['gasPrice']) formattedObj['gasPrice'] = wei(obj['gasPrice'], 0); - return formattedObj as Pick; - }); - results = results.concat(newResults); - if (newResults.length < 1000) { - break; - } - if (paginationKey) { - paginationValue = rawResults[rawResults.length - 1][paginatedOptions.orderBy!]; - } - } while (paginationKey && options.first && results.length < options.first); - return options.first ? results.slice(0, options.first) : results; -}; export type CandleFilter = { id?: string | null; id_not?: string | null; @@ -493,11 +237,17 @@ export type CrossMarginAccountFilter = { id_not_in?: string[]; owner?: string | null; owner_not?: string | null; + owner_gt?: string | null; + owner_lt?: string | null; + owner_gte?: string | null; + owner_lte?: string | null; owner_in?: string[]; owner_not_in?: string[]; owner_contains?: string | null; owner_not_contains?: string | null; _change_block?: any | null; + and?: (CrossMarginAccountFilter | null)[]; + or?: (CrossMarginAccountFilter | null)[]; }; export type CrossMarginAccountResult = { id: string; @@ -588,12 +338,20 @@ export type CrossMarginAccountTransferFilter = { id_not_in?: string[]; account?: string | null; account_not?: string | null; + account_gt?: string | null; + account_lt?: string | null; + account_gte?: string | null; + account_lte?: string | null; account_in?: string[]; account_not_in?: string[]; account_contains?: string | null; account_not_contains?: string | null; abstractAccount?: string | null; abstractAccount_not?: string | null; + abstractAccount_gt?: string | null; + abstractAccount_lt?: string | null; + abstractAccount_gte?: string | null; + abstractAccount_lte?: string | null; abstractAccount_in?: string[]; abstractAccount_not_in?: string[]; abstractAccount_contains?: string | null; @@ -635,6 +393,8 @@ export type CrossMarginAccountTransferFilter = { txHash_not_ends_with?: string | null; txHash_not_ends_with_nocase?: string | null; _change_block?: any | null; + and?: (CrossMarginAccountTransferFilter | null)[]; + or?: (CrossMarginAccountTransferFilter | null)[]; }; export type CrossMarginAccountTransferResult = { id: string; @@ -737,7 +497,7 @@ export const getCrossMarginAccountTransfers = async function < } while (paginationKey && options.first && results.length < options.first); return options.first ? results.slice(0, options.first) : results; }; -export type ExchangerFilter = { +export type FundingPaymentFilter = { id?: string | null; id_not?: string | null; id_gt?: string | null; @@ -754,131 +514,84 @@ export type ExchangerFilter = { timestamp_lte?: WeiSource | null; timestamp_in?: WeiSource[]; timestamp_not_in?: WeiSource[]; - period?: WeiSource | null; - period_not?: WeiSource | null; - period_gt?: WeiSource | null; - period_lt?: WeiSource | null; - period_gte?: WeiSource | null; - period_lte?: WeiSource | null; - period_in?: WeiSource[]; - period_not_in?: WeiSource[]; - bucketMagnitude?: WeiSource | null; - bucketMagnitude_not?: WeiSource | null; - bucketMagnitude_gt?: WeiSource | null; - bucketMagnitude_lt?: WeiSource | null; - bucketMagnitude_gte?: WeiSource | null; - bucketMagnitude_lte?: WeiSource | null; - bucketMagnitude_in?: WeiSource[]; - bucketMagnitude_not_in?: WeiSource[]; - synth?: string | null; - synth_not?: string | null; - synth_gt?: string | null; - synth_lt?: string | null; - synth_gte?: string | null; - synth_lte?: string | null; - synth_in?: string[]; - synth_not_in?: string[]; - synth_contains?: string | null; - synth_contains_nocase?: string | null; - synth_not_contains?: string | null; - synth_not_contains_nocase?: string | null; - synth_starts_with?: string | null; - synth_starts_with_nocase?: string | null; - synth_not_starts_with?: string | null; - synth_not_starts_with_nocase?: string | null; - synth_ends_with?: string | null; - synth_ends_with_nocase?: string | null; - synth_not_ends_with?: string | null; - synth_not_ends_with_nocase?: string | null; - synth_?: SynthFilter | null; - firstSeen?: WeiSource | null; - firstSeen_not?: WeiSource | null; - firstSeen_gt?: WeiSource | null; - firstSeen_lt?: WeiSource | null; - firstSeen_gte?: WeiSource | null; - firstSeen_lte?: WeiSource | null; - firstSeen_in?: WeiSource[]; - firstSeen_not_in?: WeiSource[]; - lastSeen?: WeiSource | null; - lastSeen_not?: WeiSource | null; - lastSeen_gt?: WeiSource | null; - lastSeen_lt?: WeiSource | null; - lastSeen_gte?: WeiSource | null; - lastSeen_lte?: WeiSource | null; - lastSeen_in?: WeiSource[]; - lastSeen_not_in?: WeiSource[]; - trades?: WeiSource | null; - trades_not?: WeiSource | null; - trades_gt?: WeiSource | null; - trades_lt?: WeiSource | null; - trades_gte?: WeiSource | null; - trades_lte?: WeiSource | null; - trades_in?: WeiSource[]; - trades_not_in?: WeiSource[]; - exchangeUSDTally?: WeiSource | null; - exchangeUSDTally_not?: WeiSource | null; - exchangeUSDTally_gt?: WeiSource | null; - exchangeUSDTally_lt?: WeiSource | null; - exchangeUSDTally_gte?: WeiSource | null; - exchangeUSDTally_lte?: WeiSource | null; - exchangeUSDTally_in?: WeiSource[]; - exchangeUSDTally_not_in?: WeiSource[]; - totalFeesGeneratedInUSD?: WeiSource | null; - totalFeesGeneratedInUSD_not?: WeiSource | null; - totalFeesGeneratedInUSD_gt?: WeiSource | null; - totalFeesGeneratedInUSD_lt?: WeiSource | null; - totalFeesGeneratedInUSD_gte?: WeiSource | null; - totalFeesGeneratedInUSD_lte?: WeiSource | null; - totalFeesGeneratedInUSD_in?: WeiSource[]; - totalFeesGeneratedInUSD_not_in?: WeiSource[]; - balances?: string[]; - balances_not?: string[]; - balances_contains?: string[]; - balances_contains_nocase?: string[]; - balances_not_contains?: string[]; - balances_not_contains_nocase?: string[]; - balances_?: LatestSynthBalanceFilter | null; - exchanges_?: SynthExchangeFilter | null; + account?: string | null; + account_not?: string | null; + account_gt?: string | null; + account_lt?: string | null; + account_gte?: string | null; + account_lte?: string | null; + account_in?: string[]; + account_not_in?: string[]; + account_contains?: string | null; + account_not_contains?: string | null; + positionId?: string | null; + positionId_not?: string | null; + positionId_gt?: string | null; + positionId_lt?: string | null; + positionId_gte?: string | null; + positionId_lte?: string | null; + positionId_in?: string[]; + positionId_not_in?: string[]; + marketKey?: string | null; + marketKey_not?: string | null; + marketKey_gt?: string | null; + marketKey_lt?: string | null; + marketKey_gte?: string | null; + marketKey_lte?: string | null; + marketKey_in?: string[]; + marketKey_not_in?: string[]; + marketKey_contains?: string | null; + marketKey_not_contains?: string | null; + asset?: string | null; + asset_not?: string | null; + asset_gt?: string | null; + asset_lt?: string | null; + asset_gte?: string | null; + asset_lte?: string | null; + asset_in?: string[]; + asset_not_in?: string[]; + asset_contains?: string | null; + asset_not_contains?: string | null; + amount?: WeiSource | null; + amount_not?: WeiSource | null; + amount_gt?: WeiSource | null; + amount_lt?: WeiSource | null; + amount_gte?: WeiSource | null; + amount_lte?: WeiSource | null; + amount_in?: WeiSource[]; + amount_not_in?: WeiSource[]; _change_block?: any | null; + and?: (FundingPaymentFilter | null)[]; + or?: (FundingPaymentFilter | null)[]; }; -export type ExchangerResult = { +export type FundingPaymentResult = { id: string; timestamp: Wei; - period: Wei; - bucketMagnitude: Wei; - synth: Partial | null; - firstSeen: Wei; - lastSeen: Wei; - trades: Wei; - exchangeUSDTally: Wei; - totalFeesGeneratedInUSD: Wei; - balances: Partial[]; - exchanges: Partial[]; + account: string; + positionId: string; + marketKey: string; + asset: string; + amount: Wei; }; -export type ExchangerFields = { +export type FundingPaymentFields = { id: true; timestamp: true; - period: true; - bucketMagnitude: true; - synth: SynthFields; - firstSeen: true; - lastSeen: true; - trades: true; - exchangeUSDTally: true; - totalFeesGeneratedInUSD: true; - balances: LatestSynthBalanceFields; - exchanges: SynthExchangeFields; + account: true; + positionId: true; + marketKey: true; + asset: true; + amount: true; }; -export type ExchangerArgs = { - [Property in keyof Pick]: ExchangerFields[Property]; +export type FundingPaymentArgs = { + [Property in keyof Pick]: FundingPaymentFields[Property]; }; -export const getExchangerById = async function ( +export const getFundingPaymentById = async function ( url: string, options: SingleQueryOptions, - args: ExchangerArgs -): Promise> { + args: FundingPaymentArgs +): Promise> { const res = await axios.post(url, { - query: generateGql('exchanger', options, args), + query: generateGql('fundingPayment', options, args), }); const r = res.data as any; if (r.errors && r.errors.length) { @@ -888,43 +601,37 @@ export const getExchangerById = async function const formattedObj: any = {}; if (obj['id']) formattedObj['id'] = obj['id']; if (obj['timestamp']) formattedObj['timestamp'] = wei(obj['timestamp'], 0); - if (obj['period']) formattedObj['period'] = wei(obj['period'], 0); - if (obj['bucketMagnitude']) formattedObj['bucketMagnitude'] = wei(obj['bucketMagnitude'], 0); - if (obj['synth']) formattedObj['synth'] = obj['synth']; - if (obj['firstSeen']) formattedObj['firstSeen'] = wei(obj['firstSeen'], 0); - if (obj['lastSeen']) formattedObj['lastSeen'] = wei(obj['lastSeen'], 0); - if (obj['trades']) formattedObj['trades'] = wei(obj['trades'], 0); - if (obj['exchangeUSDTally']) formattedObj['exchangeUSDTally'] = wei(obj['exchangeUSDTally']); - if (obj['totalFeesGeneratedInUSD']) - formattedObj['totalFeesGeneratedInUSD'] = wei(obj['totalFeesGeneratedInUSD']); - if (obj['balances']) formattedObj['balances'] = obj['balances']; - if (obj['exchanges']) formattedObj['exchanges'] = obj['exchanges']; - return formattedObj as Pick; + if (obj['account']) formattedObj['account'] = obj['account']; + if (obj['positionId']) formattedObj['positionId'] = obj['positionId']; + if (obj['marketKey']) formattedObj['marketKey'] = obj['marketKey']; + if (obj['asset']) formattedObj['asset'] = obj['asset']; + if (obj['amount']) formattedObj['amount'] = wei(obj['amount'], 0); + return formattedObj as Pick; }; -export const getExchangers = async function ( +export const getFundingPayments = async function ( url: string, - options: MultiQueryOptions, - args: ExchangerArgs -): Promise[]> { - const paginatedOptions: Partial> = { + options: MultiQueryOptions, + args: FundingPaymentArgs +): Promise[]> { + const paginatedOptions: Partial> = { ...options, }; - let paginationKey: keyof ExchangerFilter | null = null; + let paginationKey: keyof FundingPaymentFilter | null = null; let paginationValue = ''; if (options.first && options.first > MAX_PAGE) { paginatedOptions.first = MAX_PAGE; paginatedOptions.orderBy = options.orderBy || 'id'; paginatedOptions.orderDirection = options.orderDirection || 'asc'; paginationKey = (paginatedOptions.orderBy + - (paginatedOptions.orderDirection === 'asc' ? '_gt' : '_lt')) as keyof ExchangerFilter; + (paginatedOptions.orderDirection === 'asc' ? '_gt' : '_lt')) as keyof FundingPaymentFilter; paginatedOptions.where = { ...options.where }; } - let results: Pick[] = []; + let results: Pick[] = []; do { if (paginationKey && paginationValue) paginatedOptions.where![paginationKey] = paginationValue as any; const res = await axios.post(url, { - query: generateGql('exchangers', paginatedOptions, args), + query: generateGql('fundingPayments', paginatedOptions, args), }); const r = res.data as any; if (r.errors && r.errors.length) { @@ -935,18 +642,12 @@ export const getExchangers = async function ( const formattedObj: any = {}; if (obj['id']) formattedObj['id'] = obj['id']; if (obj['timestamp']) formattedObj['timestamp'] = wei(obj['timestamp'], 0); - if (obj['period']) formattedObj['period'] = wei(obj['period'], 0); - if (obj['bucketMagnitude']) formattedObj['bucketMagnitude'] = wei(obj['bucketMagnitude'], 0); - if (obj['synth']) formattedObj['synth'] = obj['synth']; - if (obj['firstSeen']) formattedObj['firstSeen'] = wei(obj['firstSeen'], 0); - if (obj['lastSeen']) formattedObj['lastSeen'] = wei(obj['lastSeen'], 0); - if (obj['trades']) formattedObj['trades'] = wei(obj['trades'], 0); - if (obj['exchangeUSDTally']) formattedObj['exchangeUSDTally'] = wei(obj['exchangeUSDTally']); - if (obj['totalFeesGeneratedInUSD']) - formattedObj['totalFeesGeneratedInUSD'] = wei(obj['totalFeesGeneratedInUSD']); - if (obj['balances']) formattedObj['balances'] = obj['balances']; - if (obj['exchanges']) formattedObj['exchanges'] = obj['exchanges']; - return formattedObj as Pick; + if (obj['account']) formattedObj['account'] = obj['account']; + if (obj['positionId']) formattedObj['positionId'] = obj['positionId']; + if (obj['marketKey']) formattedObj['marketKey'] = obj['marketKey']; + if (obj['asset']) formattedObj['asset'] = obj['asset']; + if (obj['amount']) formattedObj['amount'] = wei(obj['amount'], 0); + return formattedObj as Pick; }); results = results.concat(newResults); if (newResults.length < 1000) { @@ -977,10 +678,34 @@ export type FundingRateUpdateFilter = { timestamp_not_in?: WeiSource[]; market?: string | null; market_not?: string | null; + market_gt?: string | null; + market_lt?: string | null; + market_gte?: string | null; + market_lte?: string | null; market_in?: string[]; market_not_in?: string[]; market_contains?: string | null; market_not_contains?: string | null; + marketKey?: string | null; + marketKey_not?: string | null; + marketKey_gt?: string | null; + marketKey_lt?: string | null; + marketKey_gte?: string | null; + marketKey_lte?: string | null; + marketKey_in?: string[]; + marketKey_not_in?: string[]; + marketKey_contains?: string | null; + marketKey_not_contains?: string | null; + asset?: string | null; + asset_not?: string | null; + asset_gt?: string | null; + asset_lt?: string | null; + asset_gte?: string | null; + asset_lte?: string | null; + asset_in?: string[]; + asset_not_in?: string[]; + asset_contains?: string | null; + asset_not_contains?: string | null; sequenceLength?: WeiSource | null; sequenceLength_not?: WeiSource | null; sequenceLength_gt?: WeiSource | null; @@ -997,21 +722,37 @@ export type FundingRateUpdateFilter = { funding_lte?: WeiSource | null; funding_in?: WeiSource[]; funding_not_in?: WeiSource[]; + fundingRate?: WeiSource | null; + fundingRate_not?: WeiSource | null; + fundingRate_gt?: WeiSource | null; + fundingRate_lt?: WeiSource | null; + fundingRate_gte?: WeiSource | null; + fundingRate_lte?: WeiSource | null; + fundingRate_in?: WeiSource[]; + fundingRate_not_in?: WeiSource[]; _change_block?: any | null; + and?: (FundingRateUpdateFilter | null)[]; + or?: (FundingRateUpdateFilter | null)[]; }; export type FundingRateUpdateResult = { id: string; timestamp: Wei; market: string; + marketKey: string; + asset: string; sequenceLength: Wei; funding: Wei; + fundingRate: Wei; }; export type FundingRateUpdateFields = { id: true; timestamp: true; market: true; + marketKey: true; + asset: true; sequenceLength: true; funding: true; + fundingRate: true; }; export type FundingRateUpdateArgs = { [Property in keyof Pick]: FundingRateUpdateFields[Property]; @@ -1033,8 +774,11 @@ export const getFundingRateUpdateById = async function ; }; export const getFundingRateUpdates = async function ( @@ -1073,8 +817,11 @@ export const getFundingRateUpdates = async function ; }); results = results.concat(newResults); @@ -1114,12 +861,20 @@ export type FuturesAggregateStatFilter = { timestamp_not_in?: WeiSource[]; marketKey?: string | null; marketKey_not?: string | null; + marketKey_gt?: string | null; + marketKey_lt?: string | null; + marketKey_gte?: string | null; + marketKey_lte?: string | null; marketKey_in?: string[]; marketKey_not_in?: string[]; marketKey_contains?: string | null; marketKey_not_contains?: string | null; asset?: string | null; asset_not?: string | null; + asset_gt?: string | null; + asset_lt?: string | null; + asset_gte?: string | null; + asset_lte?: string | null; asset_in?: string[]; asset_not_in?: string[]; asset_contains?: string | null; @@ -1165,6 +920,8 @@ export type FuturesAggregateStatFilter = { feesCrossMarginAccounts_in?: WeiSource[]; feesCrossMarginAccounts_not_in?: WeiSource[]; _change_block?: any | null; + and?: (FuturesAggregateStatFilter | null)[]; + or?: (FuturesAggregateStatFilter | null)[]; }; export type FuturesAggregateStatResult = { id: string; @@ -1330,6 +1087,8 @@ export type FuturesCumulativeStatFilter = { averageTradeSize_in?: WeiSource[]; averageTradeSize_not_in?: WeiSource[]; _change_block?: any | null; + and?: (FuturesCumulativeStatFilter | null)[]; + or?: (FuturesCumulativeStatFilter | null)[]; }; export type FuturesCumulativeStatResult = { id: string; @@ -1451,18 +1210,30 @@ export type FuturesMarginAccountFilter = { timestamp_not_in?: WeiSource[]; account?: string | null; account_not?: string | null; + account_gt?: string | null; + account_lt?: string | null; + account_gte?: string | null; + account_lte?: string | null; account_in?: string[]; account_not_in?: string[]; account_contains?: string | null; account_not_contains?: string | null; market?: string | null; market_not?: string | null; + market_gt?: string | null; + market_lt?: string | null; + market_gte?: string | null; + market_lte?: string | null; market_in?: string[]; market_not_in?: string[]; market_contains?: string | null; market_not_contains?: string | null; asset?: string | null; asset_not?: string | null; + asset_gt?: string | null; + asset_lt?: string | null; + asset_gte?: string | null; + asset_lte?: string | null; asset_in?: string[]; asset_not_in?: string[]; asset_contains?: string | null; @@ -1492,6 +1263,8 @@ export type FuturesMarginAccountFilter = { withdrawals_in?: WeiSource[]; withdrawals_not_in?: WeiSource[]; _change_block?: any | null; + and?: (FuturesMarginAccountFilter | null)[]; + or?: (FuturesMarginAccountFilter | null)[]; }; export type FuturesMarginAccountResult = { id: string; @@ -1616,24 +1389,40 @@ export type FuturesMarginTransferFilter = { timestamp_not_in?: WeiSource[]; account?: string | null; account_not?: string | null; + account_gt?: string | null; + account_lt?: string | null; + account_gte?: string | null; + account_lte?: string | null; account_in?: string[]; account_not_in?: string[]; account_contains?: string | null; account_not_contains?: string | null; market?: string | null; market_not?: string | null; + market_gt?: string | null; + market_lt?: string | null; + market_gte?: string | null; + market_lte?: string | null; market_in?: string[]; market_not_in?: string[]; market_contains?: string | null; market_not_contains?: string | null; asset?: string | null; asset_not?: string | null; + asset_gt?: string | null; + asset_lt?: string | null; + asset_gte?: string | null; + asset_lte?: string | null; asset_in?: string[]; asset_not_in?: string[]; asset_contains?: string | null; asset_not_contains?: string | null; marketKey?: string | null; marketKey_not?: string | null; + marketKey_gt?: string | null; + marketKey_lt?: string | null; + marketKey_gte?: string | null; + marketKey_lte?: string | null; marketKey_in?: string[]; marketKey_not_in?: string[]; marketKey_contains?: string | null; @@ -1667,6 +1456,8 @@ export type FuturesMarginTransferFilter = { txHash_not_ends_with?: string | null; txHash_not_ends_with_nocase?: string | null; _change_block?: any | null; + and?: (FuturesMarginTransferFilter | null)[]; + or?: (FuturesMarginTransferFilter | null)[]; }; export type FuturesMarginTransferResult = { id: string; @@ -1785,12 +1576,20 @@ export type FuturesMarketFilter = { id_not_in?: string[]; asset?: string | null; asset_not?: string | null; + asset_gt?: string | null; + asset_lt?: string | null; + asset_gte?: string | null; + asset_lte?: string | null; asset_in?: string[]; asset_not_in?: string[]; asset_contains?: string | null; asset_not_contains?: string | null; marketKey?: string | null; marketKey_not?: string | null; + marketKey_gt?: string | null; + marketKey_lt?: string | null; + marketKey_gte?: string | null; + marketKey_lte?: string | null; marketKey_in?: string[]; marketKey_not_in?: string[]; marketKey_contains?: string | null; @@ -1817,6 +1616,8 @@ export type FuturesMarketFilter = { marketStats_not_ends_with_nocase?: string | null; marketStats_?: FuturesCumulativeStatFilter | null; _change_block?: any | null; + and?: (FuturesMarketFilter | null)[]; + or?: (FuturesMarketFilter | null)[]; }; export type FuturesMarketResult = { id: string; @@ -1920,30 +1721,50 @@ export type FuturesOrderFilter = { size_not_in?: WeiSource[]; asset?: string | null; asset_not?: string | null; + asset_gt?: string | null; + asset_lt?: string | null; + asset_gte?: string | null; + asset_lte?: string | null; asset_in?: string[]; asset_not_in?: string[]; asset_contains?: string | null; asset_not_contains?: string | null; marketKey?: string | null; marketKey_not?: string | null; + marketKey_gt?: string | null; + marketKey_lt?: string | null; + marketKey_gte?: string | null; + marketKey_lte?: string | null; marketKey_in?: string[]; marketKey_not_in?: string[]; marketKey_contains?: string | null; marketKey_not_contains?: string | null; market?: string | null; market_not?: string | null; + market_gt?: string | null; + market_lt?: string | null; + market_gte?: string | null; + market_lte?: string | null; market_in?: string[]; market_not_in?: string[]; market_contains?: string | null; market_not_contains?: string | null; account?: string | null; account_not?: string | null; + account_gt?: string | null; + account_lt?: string | null; + account_gte?: string | null; + account_lte?: string | null; account_in?: string[]; account_not_in?: string[]; account_contains?: string | null; account_not_contains?: string | null; abstractAccount?: string | null; abstractAccount_not?: string | null; + abstractAccount_gt?: string | null; + abstractAccount_lt?: string | null; + abstractAccount_gte?: string | null; + abstractAccount_lte?: string | null; abstractAccount_in?: string[]; abstractAccount_not_in?: string[]; abstractAccount_contains?: string | null; @@ -1998,11 +1819,17 @@ export type FuturesOrderFilter = { status_not_in?: FuturesOrderStatus[]; keeper?: string | null; keeper_not?: string | null; + keeper_gt?: string | null; + keeper_lt?: string | null; + keeper_gte?: string | null; + keeper_lte?: string | null; keeper_in?: string[]; keeper_not_in?: string[]; keeper_contains?: string | null; keeper_not_contains?: string | null; _change_block?: any | null; + and?: (FuturesOrderFilter | null)[]; + or?: (FuturesOrderFilter | null)[]; }; export type FuturesOrderResult = { id: string; @@ -2142,6 +1969,10 @@ export type FuturesPositionFilter = { id_not_in?: string[]; lastTxHash?: string | null; lastTxHash_not?: string | null; + lastTxHash_gt?: string | null; + lastTxHash_lt?: string | null; + lastTxHash_gte?: string | null; + lastTxHash_lte?: string | null; lastTxHash_in?: string[]; lastTxHash_not_in?: string[]; lastTxHash_contains?: string | null; @@ -2172,30 +2003,50 @@ export type FuturesPositionFilter = { timestamp_not_in?: WeiSource[]; market?: string | null; market_not?: string | null; + market_gt?: string | null; + market_lt?: string | null; + market_gte?: string | null; + market_lte?: string | null; market_in?: string[]; market_not_in?: string[]; market_contains?: string | null; market_not_contains?: string | null; asset?: string | null; asset_not?: string | null; + asset_gt?: string | null; + asset_lt?: string | null; + asset_gte?: string | null; + asset_lte?: string | null; asset_in?: string[]; asset_not_in?: string[]; asset_contains?: string | null; asset_not_contains?: string | null; marketKey?: string | null; marketKey_not?: string | null; + marketKey_gt?: string | null; + marketKey_lt?: string | null; + marketKey_gte?: string | null; + marketKey_lte?: string | null; marketKey_in?: string[]; marketKey_not_in?: string[]; marketKey_contains?: string | null; marketKey_not_contains?: string | null; account?: string | null; account_not?: string | null; + account_gt?: string | null; + account_lt?: string | null; + account_gte?: string | null; + account_lte?: string | null; account_in?: string[]; account_not_in?: string[]; account_contains?: string | null; account_not_contains?: string | null; abstractAccount?: string | null; abstractAccount_not?: string | null; + abstractAccount_gt?: string | null; + abstractAccount_lt?: string | null; + abstractAccount_gte?: string | null; + abstractAccount_lte?: string | null; abstractAccount_in?: string[]; abstractAccount_not_in?: string[]; abstractAccount_contains?: string | null; @@ -2341,6 +2192,8 @@ export type FuturesPositionFilter = { exitPrice_in?: WeiSource[]; exitPrice_not_in?: WeiSource[]; _change_block?: any | null; + and?: (FuturesPositionFilter | null)[]; + or?: (FuturesPositionFilter | null)[]; }; export type FuturesPositionResult = { id: string; @@ -2537,6 +2390,10 @@ export type FuturesStatFilter = { id_not_in?: string[]; account?: string | null; account_not?: string | null; + account_gt?: string | null; + account_lt?: string | null; + account_gte?: string | null; + account_lte?: string | null; account_in?: string[]; account_not_in?: string[]; account_contains?: string | null; @@ -2589,15 +2446,17 @@ export type FuturesStatFilter = { totalVolume_lte?: WeiSource | null; totalVolume_in?: WeiSource[]; totalVolume_not_in?: WeiSource[]; - crossMarginVolume?: WeiSource | null; - crossMarginVolume_not?: WeiSource | null; - crossMarginVolume_gt?: WeiSource | null; - crossMarginVolume_lt?: WeiSource | null; - crossMarginVolume_gte?: WeiSource | null; - crossMarginVolume_lte?: WeiSource | null; - crossMarginVolume_in?: WeiSource[]; - crossMarginVolume_not_in?: WeiSource[]; + smartMarginVolume?: WeiSource | null; + smartMarginVolume_not?: WeiSource | null; + smartMarginVolume_gt?: WeiSource | null; + smartMarginVolume_lt?: WeiSource | null; + smartMarginVolume_gte?: WeiSource | null; + smartMarginVolume_lte?: WeiSource | null; + smartMarginVolume_in?: WeiSource[]; + smartMarginVolume_not_in?: WeiSource[]; _change_block?: any | null; + and?: (FuturesStatFilter | null)[]; + or?: (FuturesStatFilter | null)[]; }; export type FuturesStatResult = { id: string; @@ -2608,7 +2467,7 @@ export type FuturesStatResult = { liquidations: Wei; totalTrades: Wei; totalVolume: Wei; - crossMarginVolume: Wei; + smartMarginVolume: Wei; }; export type FuturesStatFields = { id: true; @@ -2619,7 +2478,7 @@ export type FuturesStatFields = { liquidations: true; totalTrades: true; totalVolume: true; - crossMarginVolume: true; + smartMarginVolume: true; }; export type FuturesStatArgs = { [Property in keyof Pick]: FuturesStatFields[Property]; @@ -2646,8 +2505,8 @@ export const getFuturesStatById = async function ; }; export const getFuturesStats = async function ( @@ -2690,8 +2549,8 @@ export const getFuturesStats = async function ; }); results = results.concat(newResults); @@ -2723,12 +2582,20 @@ export type FuturesTradeFilter = { timestamp_not_in?: WeiSource[]; account?: string | null; account_not?: string | null; + account_gt?: string | null; + account_lt?: string | null; + account_gte?: string | null; + account_lte?: string | null; account_in?: string[]; account_not_in?: string[]; account_contains?: string | null; account_not_contains?: string | null; abstractAccount?: string | null; abstractAccount_not?: string | null; + abstractAccount_gt?: string | null; + abstractAccount_lt?: string | null; + abstractAccount_gte?: string | null; + abstractAccount_lte?: string | null; abstractAccount_in?: string[]; abstractAccount_not_in?: string[]; abstractAccount_contains?: string | null; @@ -2755,12 +2622,20 @@ export type FuturesTradeFilter = { size_not_in?: WeiSource[]; asset?: string | null; asset_not?: string | null; + asset_gt?: string | null; + asset_lt?: string | null; + asset_gte?: string | null; + asset_lte?: string | null; asset_in?: string[]; asset_not_in?: string[]; asset_contains?: string | null; asset_not_contains?: string | null; marketKey?: string | null; marketKey_not?: string | null; + marketKey_gt?: string | null; + marketKey_lt?: string | null; + marketKey_gte?: string | null; + marketKey_lte?: string | null; marketKey_in?: string[]; marketKey_not_in?: string[]; marketKey_contains?: string | null; @@ -2809,11 +2684,39 @@ export type FuturesTradeFilter = { feesPaid_lte?: WeiSource | null; feesPaid_in?: WeiSource[]; feesPaid_not_in?: WeiSource[]; + fundingAccrued?: WeiSource | null; + fundingAccrued_not?: WeiSource | null; + fundingAccrued_gt?: WeiSource | null; + fundingAccrued_lt?: WeiSource | null; + fundingAccrued_gte?: WeiSource | null; + fundingAccrued_lte?: WeiSource | null; + fundingAccrued_in?: WeiSource[]; + fundingAccrued_not_in?: WeiSource[]; + keeperFeesPaid?: WeiSource | null; + keeperFeesPaid_not?: WeiSource | null; + keeperFeesPaid_gt?: WeiSource | null; + keeperFeesPaid_lt?: WeiSource | null; + keeperFeesPaid_gte?: WeiSource | null; + keeperFeesPaid_lte?: WeiSource | null; + keeperFeesPaid_in?: WeiSource[]; + keeperFeesPaid_not_in?: WeiSource[]; orderType?: FuturesOrderType | null; orderType_not?: FuturesOrderType | null; orderType_in?: FuturesOrderType[]; orderType_not_in?: FuturesOrderType[]; + trackingCode?: string | null; + trackingCode_not?: string | null; + trackingCode_gt?: string | null; + trackingCode_lt?: string | null; + trackingCode_gte?: string | null; + trackingCode_lte?: string | null; + trackingCode_in?: string[]; + trackingCode_not_in?: string[]; + trackingCode_contains?: string | null; + trackingCode_not_contains?: string | null; _change_block?: any | null; + and?: (FuturesTradeFilter | null)[]; + or?: (FuturesTradeFilter | null)[]; }; export type FuturesTradeResult = { id: string; @@ -2831,7 +2734,10 @@ export type FuturesTradeResult = { positionClosed: boolean; pnl: Wei; feesPaid: Wei; + fundingAccrued: Wei; + keeperFeesPaid: Wei; orderType: Partial; + trackingCode: string; }; export type FuturesTradeFields = { id: true; @@ -2849,7 +2755,10 @@ export type FuturesTradeFields = { positionClosed: true; pnl: true; feesPaid: true; + fundingAccrued: true; + keeperFeesPaid: true; orderType: true; + trackingCode: true; }; export type FuturesTradeArgs = { [Property in keyof Pick]: FuturesTradeFields[Property]; @@ -2883,7 +2792,10 @@ export const getFuturesTradeById = async function ; }; export const getFuturesTrades = async function ( @@ -2933,7 +2845,10 @@ export const getFuturesTrades = async function ; }); results = results.concat(newResults); @@ -2946,7 +2861,7 @@ export const getFuturesTrades = async function = { - [Property in keyof Pick]: LatestRateFields[Property]; -}; -export const getLatestRateById = async function ( - url: string, - options: SingleQueryOptions, - args: LatestRateArgs -): Promise> { - const res = await axios.post(url, { - query: generateGql('latestRate', options, args), - }); - const r = res.data as any; - if (r.errors && r.errors.length) { - throw new Error(r.errors[0].message); - } - const obj = r.data[Object.keys(r.data)[0]] as any; - const formattedObj: any = {}; - if (obj['id']) formattedObj['id'] = obj['id']; - if (obj['rate']) formattedObj['rate'] = wei(obj['rate']); - if (obj['aggregator']) formattedObj['aggregator'] = obj['aggregator']; - if (obj['timestamp']) formattedObj['timestamp'] = wei(obj['timestamp'], 0); - return formattedObj as Pick; -}; -export const getLatestRates = async function ( - url: string, - options: MultiQueryOptions, - args: LatestRateArgs -): Promise[]> { - const paginatedOptions: Partial> = { - ...options, - }; - let paginationKey: keyof LatestRateFilter | null = null; - let paginationValue = ''; - if (options.first && options.first > MAX_PAGE) { - paginatedOptions.first = MAX_PAGE; - paginatedOptions.orderBy = options.orderBy || 'id'; - paginatedOptions.orderDirection = options.orderDirection || 'asc'; - paginationKey = (paginatedOptions.orderBy + - (paginatedOptions.orderDirection === 'asc' ? '_gt' : '_lt')) as keyof LatestRateFilter; - paginatedOptions.where = { ...options.where }; - } - let results: Pick[] = []; - do { - if (paginationKey && paginationValue) - paginatedOptions.where![paginationKey] = paginationValue as any; - const res = await axios.post(url, { - query: generateGql('latestRates', paginatedOptions, args), - }); - const r = res.data as any; - if (r.errors && r.errors.length) { - throw new Error(r.errors[0].message); - } - const rawResults = r.data[Object.keys(r.data)[0]] as any[]; - const newResults = rawResults.map((obj) => { - const formattedObj: any = {}; - if (obj['id']) formattedObj['id'] = obj['id']; - if (obj['rate']) formattedObj['rate'] = wei(obj['rate']); - if (obj['aggregator']) formattedObj['aggregator'] = obj['aggregator']; - if (obj['timestamp']) formattedObj['timestamp'] = wei(obj['timestamp'], 0); - return formattedObj as Pick; - }); - results = results.concat(newResults); - if (newResults.length < 1000) { - break; - } - if (paginationKey) { - paginationValue = rawResults[rawResults.length - 1][paginatedOptions.orderBy!]; - } - } while (paginationKey && options.first && results.length < options.first); - return options.first ? results.slice(0, options.first) : results; -}; -export type LatestSynthBalanceFilter = { - id?: string | null; - id_not?: string | null; - id_gt?: string | null; - id_lt?: string | null; - id_gte?: string | null; - id_lte?: string | null; - id_in?: string[]; - id_not_in?: string[]; - amount?: WeiSource | null; - amount_not?: WeiSource | null; - amount_gt?: WeiSource | null; - amount_lt?: WeiSource | null; - amount_gte?: WeiSource | null; - amount_lte?: WeiSource | null; - amount_in?: WeiSource[]; - amount_not_in?: WeiSource[]; - address?: string | null; - address_not?: string | null; - address_in?: string[]; - address_not_in?: string[]; - address_contains?: string | null; - address_not_contains?: string | null; - account?: string | null; - account_not?: string | null; - account_gt?: string | null; - account_lt?: string | null; - account_gte?: string | null; - account_lte?: string | null; - account_in?: string[]; - account_not_in?: string[]; - account_contains?: string | null; - account_contains_nocase?: string | null; - account_not_contains?: string | null; - account_not_contains_nocase?: string | null; - account_starts_with?: string | null; - account_starts_with_nocase?: string | null; - account_not_starts_with?: string | null; - account_not_starts_with_nocase?: string | null; - account_ends_with?: string | null; - account_ends_with_nocase?: string | null; - account_not_ends_with?: string | null; - account_not_ends_with_nocase?: string | null; - timestamp?: WeiSource | null; - timestamp_not?: WeiSource | null; - timestamp_gt?: WeiSource | null; - timestamp_lt?: WeiSource | null; - timestamp_gte?: WeiSource | null; - timestamp_lte?: WeiSource | null; - timestamp_in?: WeiSource[]; - timestamp_not_in?: WeiSource[]; - synth?: string | null; - synth_not?: string | null; - synth_gt?: string | null; - synth_lt?: string | null; - synth_gte?: string | null; - synth_lte?: string | null; - synth_in?: string[]; - synth_not_in?: string[]; - synth_contains?: string | null; - synth_contains_nocase?: string | null; - synth_not_contains?: string | null; - synth_not_contains_nocase?: string | null; - synth_starts_with?: string | null; - synth_starts_with_nocase?: string | null; - synth_not_starts_with?: string | null; - synth_not_starts_with_nocase?: string | null; - synth_ends_with?: string | null; - synth_ends_with_nocase?: string | null; - synth_not_ends_with?: string | null; - synth_not_ends_with_nocase?: string | null; - synth_?: SynthFilter | null; - _change_block?: any | null; -}; -export type LatestSynthBalanceResult = { - id: string; - amount: Wei; - address: string; - account: string; - timestamp: Wei; - synth: Partial | null; -}; -export type LatestSynthBalanceFields = { - id: true; - amount: true; - address: true; - account: true; - timestamp: true; - synth: SynthFields; -}; -export type LatestSynthBalanceArgs = { - [Property in keyof Pick]: LatestSynthBalanceFields[Property]; -}; -export const getLatestSynthBalanceById = async function ( - url: string, - options: SingleQueryOptions, - args: LatestSynthBalanceArgs -): Promise> { - const res = await axios.post(url, { - query: generateGql('latestSynthBalance', options, args), - }); - const r = res.data as any; - if (r.errors && r.errors.length) { - throw new Error(r.errors[0].message); - } - const obj = r.data[Object.keys(r.data)[0]] as any; - const formattedObj: any = {}; - if (obj['id']) formattedObj['id'] = obj['id']; - if (obj['amount']) formattedObj['amount'] = wei(obj['amount']); - if (obj['address']) formattedObj['address'] = obj['address']; - if (obj['account']) formattedObj['account'] = obj['account']; - if (obj['timestamp']) formattedObj['timestamp'] = wei(obj['timestamp'], 0); - if (obj['synth']) formattedObj['synth'] = obj['synth']; - return formattedObj as Pick; -}; -export const getLatestSynthBalances = async function ( - url: string, - options: MultiQueryOptions, - args: LatestSynthBalanceArgs -): Promise[]> { - const paginatedOptions: Partial> = { ...options }; - let paginationKey: keyof LatestSynthBalanceFilter | null = null; - let paginationValue = ''; - if (options.first && options.first > MAX_PAGE) { - paginatedOptions.first = MAX_PAGE; - paginatedOptions.orderBy = options.orderBy || 'id'; - paginatedOptions.orderDirection = options.orderDirection || 'asc'; - paginationKey = (paginatedOptions.orderBy + - (paginatedOptions.orderDirection === 'asc' - ? '_gt' - : '_lt')) as keyof LatestSynthBalanceFilter; - paginatedOptions.where = { ...options.where }; - } - let results: Pick[] = []; - do { - if (paginationKey && paginationValue) - paginatedOptions.where![paginationKey] = paginationValue as any; - const res = await axios.post(url, { - query: generateGql('latestSynthBalances', paginatedOptions, args), - }); - const r = res.data as any; - if (r.errors && r.errors.length) { - throw new Error(r.errors[0].message); - } - const rawResults = r.data[Object.keys(r.data)[0]] as any[]; - const newResults = rawResults.map((obj) => { - const formattedObj: any = {}; - if (obj['id']) formattedObj['id'] = obj['id']; - if (obj['amount']) formattedObj['amount'] = wei(obj['amount']); - if (obj['address']) formattedObj['address'] = obj['address']; - if (obj['account']) formattedObj['account'] = obj['account']; - if (obj['timestamp']) formattedObj['timestamp'] = wei(obj['timestamp'], 0); - if (obj['synth']) formattedObj['synth'] = obj['synth']; - return formattedObj as Pick; - }); - results = results.concat(newResults); - if (newResults.length < 1000) { - break; - } - if (paginationKey) { - paginationValue = rawResults[rawResults.length - 1][paginatedOptions.orderBy!]; - } - } while (paginationKey && options.first && results.length < options.first); - return options.first ? results.slice(0, options.first) : results; -}; -export type RateUpdateFilter = { - id?: string | null; - id_not?: string | null; - id_gt?: string | null; - id_lt?: string | null; - id_gte?: string | null; - id_lte?: string | null; - id_in?: string[]; - id_not_in?: string[]; - currencyKey?: string | null; - currencyKey_not?: string | null; - currencyKey_in?: string[]; - currencyKey_not_in?: string[]; - currencyKey_contains?: string | null; - currencyKey_not_contains?: string | null; - synth?: string | null; - synth_not?: string | null; - synth_gt?: string | null; - synth_lt?: string | null; - synth_gte?: string | null; - synth_lte?: string | null; - synth_in?: string[]; - synth_not_in?: string[]; - synth_contains?: string | null; - synth_contains_nocase?: string | null; - synth_not_contains?: string | null; - synth_not_contains_nocase?: string | null; - synth_starts_with?: string | null; - synth_starts_with_nocase?: string | null; - synth_not_starts_with?: string | null; - synth_not_starts_with_nocase?: string | null; - synth_ends_with?: string | null; - synth_ends_with_nocase?: string | null; - synth_not_ends_with?: string | null; - synth_not_ends_with_nocase?: string | null; - rate?: WeiSource | null; - rate_not?: WeiSource | null; - rate_gt?: WeiSource | null; - rate_lt?: WeiSource | null; - rate_gte?: WeiSource | null; - rate_lte?: WeiSource | null; - rate_in?: WeiSource[]; - rate_not_in?: WeiSource[]; - block?: WeiSource | null; - block_not?: WeiSource | null; - block_gt?: WeiSource | null; - block_lt?: WeiSource | null; - block_gte?: WeiSource | null; - block_lte?: WeiSource | null; - block_in?: WeiSource[]; - block_not_in?: WeiSource[]; - timestamp?: WeiSource | null; - timestamp_not?: WeiSource | null; - timestamp_gt?: WeiSource | null; - timestamp_lt?: WeiSource | null; - timestamp_gte?: WeiSource | null; - timestamp_lte?: WeiSource | null; - timestamp_in?: WeiSource[]; - timestamp_not_in?: WeiSource[]; - _change_block?: any | null; -}; -export type RateUpdateResult = { - id: string; - currencyKey: string; - synth: string; - rate: Wei; - block: Wei; - timestamp: Wei; -}; -export type RateUpdateFields = { - id: true; - currencyKey: true; - synth: true; - rate: true; - block: true; - timestamp: true; -}; -export type RateUpdateArgs = { - [Property in keyof Pick]: RateUpdateFields[Property]; -}; -export const getRateUpdateById = async function ( - url: string, - options: SingleQueryOptions, - args: RateUpdateArgs -): Promise> { - const res = await axios.post(url, { - query: generateGql('rateUpdate', options, args), - }); - const r = res.data as any; - if (r.errors && r.errors.length) { - throw new Error(r.errors[0].message); - } - const obj = r.data[Object.keys(r.data)[0]] as any; - const formattedObj: any = {}; - if (obj['id']) formattedObj['id'] = obj['id']; - if (obj['currencyKey']) formattedObj['currencyKey'] = obj['currencyKey']; - if (obj['synth']) formattedObj['synth'] = obj['synth']; - if (obj['rate']) formattedObj['rate'] = wei(obj['rate']); - if (obj['block']) formattedObj['block'] = wei(obj['block'], 0); - if (obj['timestamp']) formattedObj['timestamp'] = wei(obj['timestamp'], 0); - return formattedObj as Pick; -}; -export const getRateUpdates = async function ( - url: string, - options: MultiQueryOptions, - args: RateUpdateArgs -): Promise[]> { - const paginatedOptions: Partial> = { - ...options, - }; - let paginationKey: keyof RateUpdateFilter | null = null; - let paginationValue = ''; - if (options.first && options.first > MAX_PAGE) { - paginatedOptions.first = MAX_PAGE; - paginatedOptions.orderBy = options.orderBy || 'id'; - paginatedOptions.orderDirection = options.orderDirection || 'asc'; - paginationKey = (paginatedOptions.orderBy + - (paginatedOptions.orderDirection === 'asc' ? '_gt' : '_lt')) as keyof RateUpdateFilter; - paginatedOptions.where = { ...options.where }; - } - let results: Pick[] = []; - do { - if (paginationKey && paginationValue) - paginatedOptions.where![paginationKey] = paginationValue as any; - const res = await axios.post(url, { - query: generateGql('rateUpdates', paginatedOptions, args), - }); - const r = res.data as any; - if (r.errors && r.errors.length) { - throw new Error(r.errors[0].message); - } - const rawResults = r.data[Object.keys(r.data)[0]] as any[]; - const newResults = rawResults.map((obj) => { - const formattedObj: any = {}; - if (obj['id']) formattedObj['id'] = obj['id']; - if (obj['currencyKey']) formattedObj['currencyKey'] = obj['currencyKey']; - if (obj['synth']) formattedObj['synth'] = obj['synth']; - if (obj['rate']) formattedObj['rate'] = wei(obj['rate']); - if (obj['block']) formattedObj['block'] = wei(obj['block'], 0); - if (obj['timestamp']) formattedObj['timestamp'] = wei(obj['timestamp'], 0); - return formattedObj as Pick; - }); - results = results.concat(newResults); - if (newResults.length < 1000) { - break; - } - if (paginationKey) { - paginationValue = rawResults[rawResults.length - 1][paginatedOptions.orderBy!]; - } - } while (paginationKey && options.first && results.length < options.first); - return options.first ? results.slice(0, options.first) : results; -}; -export type SynthFilter = { - id?: string | null; - id_not?: string | null; - id_gt?: string | null; - id_lt?: string | null; - id_gte?: string | null; - id_lte?: string | null; - id_in?: string[]; - id_not_in?: string[]; - name?: string | null; - name_not?: string | null; - name_gt?: string | null; - name_lt?: string | null; - name_gte?: string | null; - name_lte?: string | null; - name_in?: string[]; - name_not_in?: string[]; - name_contains?: string | null; - name_contains_nocase?: string | null; - name_not_contains?: string | null; - name_not_contains_nocase?: string | null; - name_starts_with?: string | null; - name_starts_with_nocase?: string | null; - name_not_starts_with?: string | null; - name_not_starts_with_nocase?: string | null; - name_ends_with?: string | null; - name_ends_with_nocase?: string | null; - name_not_ends_with?: string | null; - name_not_ends_with_nocase?: string | null; - symbol?: string | null; - symbol_not?: string | null; - symbol_gt?: string | null; - symbol_lt?: string | null; - symbol_gte?: string | null; - symbol_lte?: string | null; - symbol_in?: string[]; - symbol_not_in?: string[]; - symbol_contains?: string | null; - symbol_contains_nocase?: string | null; - symbol_not_contains?: string | null; - symbol_not_contains_nocase?: string | null; - symbol_starts_with?: string | null; - symbol_starts_with_nocase?: string | null; - symbol_not_starts_with?: string | null; - symbol_not_starts_with_nocase?: string | null; - symbol_ends_with?: string | null; - symbol_ends_with_nocase?: string | null; - symbol_not_ends_with?: string | null; - symbol_not_ends_with_nocase?: string | null; - totalSupply?: WeiSource | null; - totalSupply_not?: WeiSource | null; - totalSupply_gt?: WeiSource | null; - totalSupply_lt?: WeiSource | null; - totalSupply_gte?: WeiSource | null; - totalSupply_lte?: WeiSource | null; - totalSupply_in?: WeiSource[]; - totalSupply_not_in?: WeiSource[]; - _change_block?: any | null; -}; -export type SynthResult = { - id: string; - name: string; - symbol: string; - totalSupply: Wei; -}; -export type SynthFields = { - id: true; - name: true; - symbol: true; - totalSupply: true; -}; -export type SynthArgs = { - [Property in keyof Pick]: SynthFields[Property]; -}; -export const getSynthById = async function ( - url: string, - options: SingleQueryOptions, - args: SynthArgs -): Promise> { - const res = await axios.post(url, { - query: generateGql('synth', options, args), - }); - const r = res.data as any; - if (r.errors && r.errors.length) { - throw new Error(r.errors[0].message); - } - const obj = r.data[Object.keys(r.data)[0]] as any; - const formattedObj: any = {}; - if (obj['id']) formattedObj['id'] = obj['id']; - if (obj['name']) formattedObj['name'] = obj['name']; - if (obj['symbol']) formattedObj['symbol'] = obj['symbol']; - if (obj['totalSupply']) formattedObj['totalSupply'] = wei(obj['totalSupply']); - return formattedObj as Pick; -}; -export const getSynths = async function ( - url: string, - options: MultiQueryOptions, - args: SynthArgs -): Promise[]> { - const paginatedOptions: Partial> = { ...options }; - let paginationKey: keyof SynthFilter | null = null; - let paginationValue = ''; - if (options.first && options.first > MAX_PAGE) { - paginatedOptions.first = MAX_PAGE; - paginatedOptions.orderBy = options.orderBy || 'id'; - paginatedOptions.orderDirection = options.orderDirection || 'asc'; - paginationKey = (paginatedOptions.orderBy + - (paginatedOptions.orderDirection === 'asc' ? '_gt' : '_lt')) as keyof SynthFilter; - paginatedOptions.where = { ...options.where }; - } - let results: Pick[] = []; - do { - if (paginationKey && paginationValue) - paginatedOptions.where![paginationKey] = paginationValue as any; - const res = await axios.post(url, { - query: generateGql('synths', paginatedOptions, args), - }); - const r = res.data as any; - if (r.errors && r.errors.length) { - throw new Error(r.errors[0].message); - } - const rawResults = r.data[Object.keys(r.data)[0]] as any[]; - const newResults = rawResults.map((obj) => { - const formattedObj: any = {}; - if (obj['id']) formattedObj['id'] = obj['id']; - if (obj['name']) formattedObj['name'] = obj['name']; - if (obj['symbol']) formattedObj['symbol'] = obj['symbol']; - if (obj['totalSupply']) formattedObj['totalSupply'] = wei(obj['totalSupply']); - return formattedObj as Pick; - }); - results = results.concat(newResults); - if (newResults.length < 1000) { - break; - } - if (paginationKey) { - paginationValue = rawResults[rawResults.length - 1][paginatedOptions.orderBy!]; - } - } while (paginationKey && options.first && results.length < options.first); - return options.first ? results.slice(0, options.first) : results; -}; -export type SynthBalanceFilter = { - id?: string | null; - id_not?: string | null; - id_gt?: string | null; - id_lt?: string | null; - id_gte?: string | null; - id_lte?: string | null; - id_in?: string[]; - id_not_in?: string[]; - amount?: WeiSource | null; - amount_not?: WeiSource | null; - amount_gt?: WeiSource | null; - amount_lt?: WeiSource | null; - amount_gte?: WeiSource | null; - amount_lte?: WeiSource | null; - amount_in?: WeiSource[]; - amount_not_in?: WeiSource[]; - address?: string | null; - address_not?: string | null; - address_in?: string[]; - address_not_in?: string[]; - address_contains?: string | null; - address_not_contains?: string | null; - account?: string | null; - account_not?: string | null; - account_gt?: string | null; - account_lt?: string | null; - account_gte?: string | null; - account_lte?: string | null; - account_in?: string[]; - account_not_in?: string[]; - account_contains?: string | null; - account_contains_nocase?: string | null; - account_not_contains?: string | null; - account_not_contains_nocase?: string | null; - account_starts_with?: string | null; - account_starts_with_nocase?: string | null; - account_not_starts_with?: string | null; - account_not_starts_with_nocase?: string | null; - account_ends_with?: string | null; - account_ends_with_nocase?: string | null; - account_not_ends_with?: string | null; - account_not_ends_with_nocase?: string | null; - timestamp?: WeiSource | null; - timestamp_not?: WeiSource | null; - timestamp_gt?: WeiSource | null; - timestamp_lt?: WeiSource | null; - timestamp_gte?: WeiSource | null; - timestamp_lte?: WeiSource | null; - timestamp_in?: WeiSource[]; - timestamp_not_in?: WeiSource[]; - synth?: string | null; - synth_not?: string | null; - synth_gt?: string | null; - synth_lt?: string | null; - synth_gte?: string | null; - synth_lte?: string | null; - synth_in?: string[]; - synth_not_in?: string[]; - synth_contains?: string | null; - synth_contains_nocase?: string | null; - synth_not_contains?: string | null; - synth_not_contains_nocase?: string | null; - synth_starts_with?: string | null; - synth_starts_with_nocase?: string | null; - synth_not_starts_with?: string | null; - synth_not_starts_with_nocase?: string | null; - synth_ends_with?: string | null; - synth_ends_with_nocase?: string | null; - synth_not_ends_with?: string | null; - synth_not_ends_with_nocase?: string | null; - synth_?: SynthFilter | null; - _change_block?: any | null; -}; -export type SynthBalanceResult = { - id: string; - amount: Wei; - address: string; - account: string; - timestamp: Wei; - synth: Partial | null; -}; -export type SynthBalanceFields = { - id: true; - amount: true; - address: true; - account: true; - timestamp: true; - synth: SynthFields; -}; -export type SynthBalanceArgs = { - [Property in keyof Pick]: SynthBalanceFields[Property]; -}; -export const getSynthBalanceById = async function ( - url: string, - options: SingleQueryOptions, - args: SynthBalanceArgs -): Promise> { - const res = await axios.post(url, { - query: generateGql('synthBalance', options, args), - }); - const r = res.data as any; - if (r.errors && r.errors.length) { - throw new Error(r.errors[0].message); - } - const obj = r.data[Object.keys(r.data)[0]] as any; - const formattedObj: any = {}; - if (obj['id']) formattedObj['id'] = obj['id']; - if (obj['amount']) formattedObj['amount'] = wei(obj['amount']); - if (obj['address']) formattedObj['address'] = obj['address']; - if (obj['account']) formattedObj['account'] = obj['account']; - if (obj['timestamp']) formattedObj['timestamp'] = wei(obj['timestamp'], 0); - if (obj['synth']) formattedObj['synth'] = obj['synth']; - return formattedObj as Pick; -}; -export const getSynthBalances = async function ( - url: string, - options: MultiQueryOptions, - args: SynthBalanceArgs -): Promise[]> { - const paginatedOptions: Partial> = { - ...options, - }; - let paginationKey: keyof SynthBalanceFilter | null = null; - let paginationValue = ''; - if (options.first && options.first > MAX_PAGE) { - paginatedOptions.first = MAX_PAGE; - paginatedOptions.orderBy = options.orderBy || 'id'; - paginatedOptions.orderDirection = options.orderDirection || 'asc'; - paginationKey = (paginatedOptions.orderBy + - (paginatedOptions.orderDirection === 'asc' ? '_gt' : '_lt')) as keyof SynthBalanceFilter; - paginatedOptions.where = { ...options.where }; - } - let results: Pick[] = []; - do { - if (paginationKey && paginationValue) - paginatedOptions.where![paginationKey] = paginationValue as any; - const res = await axios.post(url, { - query: generateGql('synthBalances', paginatedOptions, args), - }); - const r = res.data as any; - if (r.errors && r.errors.length) { - throw new Error(r.errors[0].message); - } - const rawResults = r.data[Object.keys(r.data)[0]] as any[]; - const newResults = rawResults.map((obj) => { - const formattedObj: any = {}; - if (obj['id']) formattedObj['id'] = obj['id']; - if (obj['amount']) formattedObj['amount'] = wei(obj['amount']); - if (obj['address']) formattedObj['address'] = obj['address']; - if (obj['account']) formattedObj['account'] = obj['account']; - if (obj['timestamp']) formattedObj['timestamp'] = wei(obj['timestamp'], 0); - if (obj['synth']) formattedObj['synth'] = obj['synth']; - return formattedObj as Pick; - }); - results = results.concat(newResults); - if (newResults.length < 1000) { - break; - } - if (paginationKey) { - paginationValue = rawResults[rawResults.length - 1][paginatedOptions.orderBy!]; - } - } while (paginationKey && options.first && results.length < options.first); - return options.first ? results.slice(0, options.first) : results; -}; -export type SynthByCurrencyKeyFilter = { - id?: string | null; - id_not?: string | null; - id_gt?: string | null; - id_lt?: string | null; - id_gte?: string | null; - id_lte?: string | null; - id_in?: string[]; - id_not_in?: string[]; - proxyAddress?: string | null; - proxyAddress_not?: string | null; - proxyAddress_in?: string[]; - proxyAddress_not_in?: string[]; - proxyAddress_contains?: string | null; - proxyAddress_not_contains?: string | null; - _change_block?: any | null; -}; -export type SynthByCurrencyKeyResult = { - id: string; - proxyAddress: string; -}; -export type SynthByCurrencyKeyFields = { - id: true; - proxyAddress: true; + owner: true; + version: true; }; -export type SynthByCurrencyKeyArgs = { - [Property in keyof Pick]: SynthByCurrencyKeyFields[Property]; +export type SmartMarginAccountArgs = { + [Property in keyof Pick]: SmartMarginAccountFields[Property]; }; -export const getSynthByCurrencyKeyById = async function ( +export const getSmartMarginAccountById = async function ( url: string, options: SingleQueryOptions, - args: SynthByCurrencyKeyArgs -): Promise> { + args: SmartMarginAccountArgs +): Promise> { const res = await axios.post(url, { - query: generateGql('synthByCurrencyKey', options, args), + query: generateGql('smartMarginAccount', options, args), }); const r = res.data as any; if (r.errors && r.errors.length) { @@ -3730,19 +2922,20 @@ export const getSynthByCurrencyKeyById = async function ; + if (obj['owner']) formattedObj['owner'] = obj['owner']; + if (obj['version']) formattedObj['version'] = obj['version']; + return formattedObj as Pick; }; -export const getSynthByCurrencyKeys = async function ( +export const getSmartMarginAccounts = async function ( url: string, - options: MultiQueryOptions, - args: SynthByCurrencyKeyArgs -): Promise[]> { + options: MultiQueryOptions, + args: SmartMarginAccountArgs +): Promise[]> { const paginatedOptions: Partial> = { ...options }; - let paginationKey: keyof SynthByCurrencyKeyFilter | null = null; + let paginationKey: keyof SmartMarginAccountFilter | null = null; let paginationValue = ''; if (options.first && options.first > MAX_PAGE) { paginatedOptions.first = MAX_PAGE; @@ -3751,460 +2944,15 @@ export const getSynthByCurrencyKeys = async function [] = []; - do { - if (paginationKey && paginationValue) - paginatedOptions.where![paginationKey] = paginationValue as any; - const res = await axios.post(url, { - query: generateGql('synthByCurrencyKeys', paginatedOptions, args), - }); - const r = res.data as any; - if (r.errors && r.errors.length) { - throw new Error(r.errors[0].message); - } - const rawResults = r.data[Object.keys(r.data)[0]] as any[]; - const newResults = rawResults.map((obj) => { - const formattedObj: any = {}; - if (obj['id']) formattedObj['id'] = obj['id']; - if (obj['proxyAddress']) formattedObj['proxyAddress'] = obj['proxyAddress']; - return formattedObj as Pick; - }); - results = results.concat(newResults); - if (newResults.length < 1000) { - break; - } - if (paginationKey) { - paginationValue = rawResults[rawResults.length - 1][paginatedOptions.orderBy!]; - } - } while (paginationKey && options.first && results.length < options.first); - return options.first ? results.slice(0, options.first) : results; -}; -export type SynthExchangeFilter = { - id?: string | null; - id_not?: string | null; - id_gt?: string | null; - id_lt?: string | null; - id_gte?: string | null; - id_lte?: string | null; - id_in?: string[]; - id_not_in?: string[]; - account?: string | null; - account_not?: string | null; - account_gt?: string | null; - account_lt?: string | null; - account_gte?: string | null; - account_lte?: string | null; - account_in?: string[]; - account_not_in?: string[]; - account_contains?: string | null; - account_contains_nocase?: string | null; - account_not_contains?: string | null; - account_not_contains_nocase?: string | null; - account_starts_with?: string | null; - account_starts_with_nocase?: string | null; - account_not_starts_with?: string | null; - account_not_starts_with_nocase?: string | null; - account_ends_with?: string | null; - account_ends_with_nocase?: string | null; - account_not_ends_with?: string | null; - account_not_ends_with_nocase?: string | null; - account_?: ExchangerFilter | null; - fromSynth?: string | null; - fromSynth_not?: string | null; - fromSynth_gt?: string | null; - fromSynth_lt?: string | null; - fromSynth_gte?: string | null; - fromSynth_lte?: string | null; - fromSynth_in?: string[]; - fromSynth_not_in?: string[]; - fromSynth_contains?: string | null; - fromSynth_contains_nocase?: string | null; - fromSynth_not_contains?: string | null; - fromSynth_not_contains_nocase?: string | null; - fromSynth_starts_with?: string | null; - fromSynth_starts_with_nocase?: string | null; - fromSynth_not_starts_with?: string | null; - fromSynth_not_starts_with_nocase?: string | null; - fromSynth_ends_with?: string | null; - fromSynth_ends_with_nocase?: string | null; - fromSynth_not_ends_with?: string | null; - fromSynth_not_ends_with_nocase?: string | null; - fromSynth_?: SynthFilter | null; - toSynth?: string | null; - toSynth_not?: string | null; - toSynth_gt?: string | null; - toSynth_lt?: string | null; - toSynth_gte?: string | null; - toSynth_lte?: string | null; - toSynth_in?: string[]; - toSynth_not_in?: string[]; - toSynth_contains?: string | null; - toSynth_contains_nocase?: string | null; - toSynth_not_contains?: string | null; - toSynth_not_contains_nocase?: string | null; - toSynth_starts_with?: string | null; - toSynth_starts_with_nocase?: string | null; - toSynth_not_starts_with?: string | null; - toSynth_not_starts_with_nocase?: string | null; - toSynth_ends_with?: string | null; - toSynth_ends_with_nocase?: string | null; - toSynth_not_ends_with?: string | null; - toSynth_not_ends_with_nocase?: string | null; - toSynth_?: SynthFilter | null; - fromAmount?: WeiSource | null; - fromAmount_not?: WeiSource | null; - fromAmount_gt?: WeiSource | null; - fromAmount_lt?: WeiSource | null; - fromAmount_gte?: WeiSource | null; - fromAmount_lte?: WeiSource | null; - fromAmount_in?: WeiSource[]; - fromAmount_not_in?: WeiSource[]; - fromAmountInUSD?: WeiSource | null; - fromAmountInUSD_not?: WeiSource | null; - fromAmountInUSD_gt?: WeiSource | null; - fromAmountInUSD_lt?: WeiSource | null; - fromAmountInUSD_gte?: WeiSource | null; - fromAmountInUSD_lte?: WeiSource | null; - fromAmountInUSD_in?: WeiSource[]; - fromAmountInUSD_not_in?: WeiSource[]; - toAmount?: WeiSource | null; - toAmount_not?: WeiSource | null; - toAmount_gt?: WeiSource | null; - toAmount_lt?: WeiSource | null; - toAmount_gte?: WeiSource | null; - toAmount_lte?: WeiSource | null; - toAmount_in?: WeiSource[]; - toAmount_not_in?: WeiSource[]; - toAmountInUSD?: WeiSource | null; - toAmountInUSD_not?: WeiSource | null; - toAmountInUSD_gt?: WeiSource | null; - toAmountInUSD_lt?: WeiSource | null; - toAmountInUSD_gte?: WeiSource | null; - toAmountInUSD_lte?: WeiSource | null; - toAmountInUSD_in?: WeiSource[]; - toAmountInUSD_not_in?: WeiSource[]; - feesInUSD?: WeiSource | null; - feesInUSD_not?: WeiSource | null; - feesInUSD_gt?: WeiSource | null; - feesInUSD_lt?: WeiSource | null; - feesInUSD_gte?: WeiSource | null; - feesInUSD_lte?: WeiSource | null; - feesInUSD_in?: WeiSource[]; - feesInUSD_not_in?: WeiSource[]; - toAddress?: string | null; - toAddress_not?: string | null; - toAddress_in?: string[]; - toAddress_not_in?: string[]; - toAddress_contains?: string | null; - toAddress_not_contains?: string | null; - timestamp?: WeiSource | null; - timestamp_not?: WeiSource | null; - timestamp_gt?: WeiSource | null; - timestamp_lt?: WeiSource | null; - timestamp_gte?: WeiSource | null; - timestamp_lte?: WeiSource | null; - timestamp_in?: WeiSource[]; - timestamp_not_in?: WeiSource[]; - gasPrice?: WeiSource | null; - gasPrice_not?: WeiSource | null; - gasPrice_gt?: WeiSource | null; - gasPrice_lt?: WeiSource | null; - gasPrice_gte?: WeiSource | null; - gasPrice_lte?: WeiSource | null; - gasPrice_in?: WeiSource[]; - gasPrice_not_in?: WeiSource[]; - _change_block?: any | null; -}; -export type SynthExchangeResult = { - id: string; - account: Partial; - fromSynth: Partial | null; - toSynth: Partial | null; - fromAmount: Wei; - fromAmountInUSD: Wei; - toAmount: Wei; - toAmountInUSD: Wei; - feesInUSD: Wei; - toAddress: string; - timestamp: Wei; - gasPrice: Wei; -}; -export type SynthExchangeFields = { - id: true; - account: ExchangerFields; - fromSynth: SynthFields; - toSynth: SynthFields; - fromAmount: true; - fromAmountInUSD: true; - toAmount: true; - toAmountInUSD: true; - feesInUSD: true; - toAddress: true; - timestamp: true; - gasPrice: true; -}; -export type SynthExchangeArgs = { - [Property in keyof Pick]: SynthExchangeFields[Property]; -}; -export const getSynthExchangeById = async function ( - url: string, - options: SingleQueryOptions, - args: SynthExchangeArgs -): Promise> { - const res = await axios.post(url, { - query: generateGql('synthExchange', options, args), - }); - const r = res.data as any; - if (r.errors && r.errors.length) { - throw new Error(r.errors[0].message); - } - const obj = r.data[Object.keys(r.data)[0]] as any; - const formattedObj: any = {}; - if (obj['id']) formattedObj['id'] = obj['id']; - if (obj['account']) formattedObj['account'] = obj['account']; - if (obj['fromSynth']) formattedObj['fromSynth'] = obj['fromSynth']; - if (obj['toSynth']) formattedObj['toSynth'] = obj['toSynth']; - if (obj['fromAmount']) formattedObj['fromAmount'] = wei(obj['fromAmount']); - if (obj['fromAmountInUSD']) formattedObj['fromAmountInUSD'] = wei(obj['fromAmountInUSD']); - if (obj['toAmount']) formattedObj['toAmount'] = wei(obj['toAmount']); - if (obj['toAmountInUSD']) formattedObj['toAmountInUSD'] = wei(obj['toAmountInUSD']); - if (obj['feesInUSD']) formattedObj['feesInUSD'] = wei(obj['feesInUSD']); - if (obj['toAddress']) formattedObj['toAddress'] = obj['toAddress']; - if (obj['timestamp']) formattedObj['timestamp'] = wei(obj['timestamp'], 0); - if (obj['gasPrice']) formattedObj['gasPrice'] = wei(obj['gasPrice'], 0); - return formattedObj as Pick; -}; -export const getSynthExchanges = async function ( - url: string, - options: MultiQueryOptions, - args: SynthExchangeArgs -): Promise[]> { - const paginatedOptions: Partial> = { - ...options, - }; - let paginationKey: keyof SynthExchangeFilter | null = null; - let paginationValue = ''; - if (options.first && options.first > MAX_PAGE) { - paginatedOptions.first = MAX_PAGE; - paginatedOptions.orderBy = options.orderBy || 'id'; - paginatedOptions.orderDirection = options.orderDirection || 'asc'; - paginationKey = (paginatedOptions.orderBy + - (paginatedOptions.orderDirection === 'asc' ? '_gt' : '_lt')) as keyof SynthExchangeFilter; - paginatedOptions.where = { ...options.where }; - } - let results: Pick[] = []; - do { - if (paginationKey && paginationValue) - paginatedOptions.where![paginationKey] = paginationValue as any; - const res = await axios.post(url, { - query: generateGql('synthExchanges', paginatedOptions, args), - }); - const r = res.data as any; - if (r.errors && r.errors.length) { - throw new Error(r.errors[0].message); - } - const rawResults = r.data[Object.keys(r.data)[0]] as any[]; - const newResults = rawResults.map((obj) => { - const formattedObj: any = {}; - if (obj['id']) formattedObj['id'] = obj['id']; - if (obj['account']) formattedObj['account'] = obj['account']; - if (obj['fromSynth']) formattedObj['fromSynth'] = obj['fromSynth']; - if (obj['toSynth']) formattedObj['toSynth'] = obj['toSynth']; - if (obj['fromAmount']) formattedObj['fromAmount'] = wei(obj['fromAmount']); - if (obj['fromAmountInUSD']) formattedObj['fromAmountInUSD'] = wei(obj['fromAmountInUSD']); - if (obj['toAmount']) formattedObj['toAmount'] = wei(obj['toAmount']); - if (obj['toAmountInUSD']) formattedObj['toAmountInUSD'] = wei(obj['toAmountInUSD']); - if (obj['feesInUSD']) formattedObj['feesInUSD'] = wei(obj['feesInUSD']); - if (obj['toAddress']) formattedObj['toAddress'] = obj['toAddress']; - if (obj['timestamp']) formattedObj['timestamp'] = wei(obj['timestamp'], 0); - if (obj['gasPrice']) formattedObj['gasPrice'] = wei(obj['gasPrice'], 0); - return formattedObj as Pick; - }); - results = results.concat(newResults); - if (newResults.length < 1000) { - break; - } - if (paginationKey) { - paginationValue = rawResults[rawResults.length - 1][paginatedOptions.orderBy!]; - } - } while (paginationKey && options.first && results.length < options.first); - return options.first ? results.slice(0, options.first) : results; -}; -export type TotalFilter = { - id?: string | null; - id_not?: string | null; - id_gt?: string | null; - id_lt?: string | null; - id_gte?: string | null; - id_lte?: string | null; - id_in?: string[]; - id_not_in?: string[]; - timestamp?: WeiSource | null; - timestamp_not?: WeiSource | null; - timestamp_gt?: WeiSource | null; - timestamp_lt?: WeiSource | null; - timestamp_gte?: WeiSource | null; - timestamp_lte?: WeiSource | null; - timestamp_in?: WeiSource[]; - timestamp_not_in?: WeiSource[]; - period?: WeiSource | null; - period_not?: WeiSource | null; - period_gt?: WeiSource | null; - period_lt?: WeiSource | null; - period_gte?: WeiSource | null; - period_lte?: WeiSource | null; - period_in?: WeiSource[]; - period_not_in?: WeiSource[]; - bucketMagnitude?: WeiSource | null; - bucketMagnitude_not?: WeiSource | null; - bucketMagnitude_gt?: WeiSource | null; - bucketMagnitude_lt?: WeiSource | null; - bucketMagnitude_gte?: WeiSource | null; - bucketMagnitude_lte?: WeiSource | null; - bucketMagnitude_in?: WeiSource[]; - bucketMagnitude_not_in?: WeiSource[]; - synth?: string | null; - synth_not?: string | null; - synth_gt?: string | null; - synth_lt?: string | null; - synth_gte?: string | null; - synth_lte?: string | null; - synth_in?: string[]; - synth_not_in?: string[]; - synth_contains?: string | null; - synth_contains_nocase?: string | null; - synth_not_contains?: string | null; - synth_not_contains_nocase?: string | null; - synth_starts_with?: string | null; - synth_starts_with_nocase?: string | null; - synth_not_starts_with?: string | null; - synth_not_starts_with_nocase?: string | null; - synth_ends_with?: string | null; - synth_ends_with_nocase?: string | null; - synth_not_ends_with?: string | null; - synth_not_ends_with_nocase?: string | null; - synth_?: SynthFilter | null; - trades?: WeiSource | null; - trades_not?: WeiSource | null; - trades_gt?: WeiSource | null; - trades_lt?: WeiSource | null; - trades_gte?: WeiSource | null; - trades_lte?: WeiSource | null; - trades_in?: WeiSource[]; - trades_not_in?: WeiSource[]; - newExchangers?: WeiSource | null; - newExchangers_not?: WeiSource | null; - newExchangers_gt?: WeiSource | null; - newExchangers_lt?: WeiSource | null; - newExchangers_gte?: WeiSource | null; - newExchangers_lte?: WeiSource | null; - newExchangers_in?: WeiSource[]; - newExchangers_not_in?: WeiSource[]; - exchangers?: WeiSource | null; - exchangers_not?: WeiSource | null; - exchangers_gt?: WeiSource | null; - exchangers_lt?: WeiSource | null; - exchangers_gte?: WeiSource | null; - exchangers_lte?: WeiSource | null; - exchangers_in?: WeiSource[]; - exchangers_not_in?: WeiSource[]; - exchangeUSDTally?: WeiSource | null; - exchangeUSDTally_not?: WeiSource | null; - exchangeUSDTally_gt?: WeiSource | null; - exchangeUSDTally_lt?: WeiSource | null; - exchangeUSDTally_gte?: WeiSource | null; - exchangeUSDTally_lte?: WeiSource | null; - exchangeUSDTally_in?: WeiSource[]; - exchangeUSDTally_not_in?: WeiSource[]; - totalFeesGeneratedInUSD?: WeiSource | null; - totalFeesGeneratedInUSD_not?: WeiSource | null; - totalFeesGeneratedInUSD_gt?: WeiSource | null; - totalFeesGeneratedInUSD_lt?: WeiSource | null; - totalFeesGeneratedInUSD_gte?: WeiSource | null; - totalFeesGeneratedInUSD_lte?: WeiSource | null; - totalFeesGeneratedInUSD_in?: WeiSource[]; - totalFeesGeneratedInUSD_not_in?: WeiSource[]; - _change_block?: any | null; -}; -export type TotalResult = { - id: string; - timestamp: Wei; - period: Wei; - bucketMagnitude: Wei; - synth: Partial | null; - trades: Wei; - newExchangers: Wei; - exchangers: Wei; - exchangeUSDTally: Wei; - totalFeesGeneratedInUSD: Wei; -}; -export type TotalFields = { - id: true; - timestamp: true; - period: true; - bucketMagnitude: true; - synth: SynthFields; - trades: true; - newExchangers: true; - exchangers: true; - exchangeUSDTally: true; - totalFeesGeneratedInUSD: true; -}; -export type TotalArgs = { - [Property in keyof Pick]: TotalFields[Property]; -}; -export const getTotalById = async function ( - url: string, - options: SingleQueryOptions, - args: TotalArgs -): Promise> { - const res = await axios.post(url, { - query: generateGql('total', options, args), - }); - const r = res.data as any; - if (r.errors && r.errors.length) { - throw new Error(r.errors[0].message); - } - const obj = r.data[Object.keys(r.data)[0]] as any; - const formattedObj: any = {}; - if (obj['id']) formattedObj['id'] = obj['id']; - if (obj['timestamp']) formattedObj['timestamp'] = wei(obj['timestamp'], 0); - if (obj['period']) formattedObj['period'] = wei(obj['period'], 0); - if (obj['bucketMagnitude']) formattedObj['bucketMagnitude'] = wei(obj['bucketMagnitude'], 0); - if (obj['synth']) formattedObj['synth'] = obj['synth']; - if (obj['trades']) formattedObj['trades'] = wei(obj['trades'], 0); - if (obj['newExchangers']) formattedObj['newExchangers'] = wei(obj['newExchangers'], 0); - if (obj['exchangers']) formattedObj['exchangers'] = wei(obj['exchangers'], 0); - if (obj['exchangeUSDTally']) formattedObj['exchangeUSDTally'] = wei(obj['exchangeUSDTally']); - if (obj['totalFeesGeneratedInUSD']) - formattedObj['totalFeesGeneratedInUSD'] = wei(obj['totalFeesGeneratedInUSD']); - return formattedObj as Pick; -}; -export const getTotals = async function ( - url: string, - options: MultiQueryOptions, - args: TotalArgs -): Promise[]> { - const paginatedOptions: Partial> = { ...options }; - let paginationKey: keyof TotalFilter | null = null; - let paginationValue = ''; - if (options.first && options.first > MAX_PAGE) { - paginatedOptions.first = MAX_PAGE; - paginatedOptions.orderBy = options.orderBy || 'id'; - paginatedOptions.orderDirection = options.orderDirection || 'asc'; - paginationKey = (paginatedOptions.orderBy + - (paginatedOptions.orderDirection === 'asc' ? '_gt' : '_lt')) as keyof TotalFilter; + : '_lt')) as keyof SmartMarginAccountFilter; paginatedOptions.where = { ...options.where }; } - let results: Pick[] = []; + let results: Pick[] = []; do { if (paginationKey && paginationValue) paginatedOptions.where![paginationKey] = paginationValue as any; const res = await axios.post(url, { - query: generateGql('totals', paginatedOptions, args), + query: generateGql('smartMarginAccounts', paginatedOptions, args), }); const r = res.data as any; if (r.errors && r.errors.length) { @@ -4214,17 +2962,9 @@ export const getTotals = async function ( const newResults = rawResults.map((obj) => { const formattedObj: any = {}; if (obj['id']) formattedObj['id'] = obj['id']; - if (obj['timestamp']) formattedObj['timestamp'] = wei(obj['timestamp'], 0); - if (obj['period']) formattedObj['period'] = wei(obj['period'], 0); - if (obj['bucketMagnitude']) formattedObj['bucketMagnitude'] = wei(obj['bucketMagnitude'], 0); - if (obj['synth']) formattedObj['synth'] = obj['synth']; - if (obj['trades']) formattedObj['trades'] = wei(obj['trades'], 0); - if (obj['newExchangers']) formattedObj['newExchangers'] = wei(obj['newExchangers'], 0); - if (obj['exchangers']) formattedObj['exchangers'] = wei(obj['exchangers'], 0); - if (obj['exchangeUSDTally']) formattedObj['exchangeUSDTally'] = wei(obj['exchangeUSDTally']); - if (obj['totalFeesGeneratedInUSD']) - formattedObj['totalFeesGeneratedInUSD'] = wei(obj['totalFeesGeneratedInUSD']); - return formattedObj as Pick; + if (obj['owner']) formattedObj['owner'] = obj['owner']; + if (obj['version']) formattedObj['version'] = obj['version']; + return formattedObj as Pick; }); results = results.concat(newResults); if (newResults.length < 1000) { @@ -4238,7 +2978,7 @@ export const getTotals = async function ( }; // additional types -export type FuturesAccountType = 'isolated_margin' | 'cross_margin'; +export type FuturesAccountType = 'isolated_margin' | 'cross_margin' | 'smart_margin'; export type FuturesOrderType = | 'NextPrice' | 'Limit' diff --git a/queries/futures/types.ts b/queries/futures/types.ts index e8aa920463..750f6a6556 100644 --- a/queries/futures/types.ts +++ b/queries/futures/types.ts @@ -1,8 +1,8 @@ -import { Balances } from '@synthetixio/queries'; import Wei from '@synthetixio/wei'; import { BigNumber } from 'ethers'; import { FuturesMarketAsset, FuturesPotentialTradeDetails } from 'sdk/types/futures'; +import { Balances } from 'sdk/types/synths'; export type FuturesOpenInterest = { asset: string; @@ -84,7 +84,6 @@ export type FuturesPotentialTradeDetailsQuery = { status: 'fetching' | 'complete' | 'idle' | 'error'; }; -export type FuturesAccountType = 'cross_margin' | 'isolated_margin'; export enum FuturesAccountTypes { ISOLATED_MARGIN = 'isolated_margin', CROSS_MARGIN = 'cross_margin', diff --git a/queries/futures/useFuturesSuspensionQuery.ts b/queries/futures/useFuturesSuspensionQuery.ts deleted file mode 100644 index b569b31a2e..0000000000 --- a/queries/futures/useFuturesSuspensionQuery.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; -import { ethers } from 'ethers'; -import { useQuery, UseQueryOptions } from 'react-query'; - -import QUERY_KEYS from 'constants/queryKeys'; -import Connector from 'containers/Connector'; -import { FuturesClosureReason } from 'hooks/useFuturesMarketClosed'; -import useIsL2 from 'hooks/useIsL2'; -import { getReasonFromCode } from 'queries/futures/utils'; - -interface FuturesMarketClosure { - isSuspended: boolean; - reasonCode: ethers.BigNumber; - reason: FuturesClosureReason; -} - -const useFuturesSuspensionQuery = ( - marketKey: string | null, - options?: UseQueryOptions -) => { - const { defaultSynthetixjs: synthetixjs, network, isWalletConnected } = Connector.useContainer(); - const isL2 = useIsL2(); - - return useQuery( - QUERY_KEYS.Futures.MarketClosure(network?.id as NetworkId, marketKey), - async () => { - try { - const { - contracts: { SystemStatus }, - utils, - } = synthetixjs!; - - if (!marketKey) return null; - - const marketKeyBytes32 = utils.formatBytes32String(marketKey); - const [isSuspended, reasonCode] = await SystemStatus.futuresMarketSuspension( - marketKeyBytes32 - ); - - const reason = (isSuspended ? getReasonFromCode(reasonCode) : null) as FuturesClosureReason; - return { - isFuturesMarketClosed: isSuspended, - reasonCode, - futuresClosureReason: reason, - }; - } catch (e) { - return null; - } - }, - { - enabled: isWalletConnected ? isL2 && !!synthetixjs : !!synthetixjs, - ...options, - } - ); -}; - -export default useFuturesSuspensionQuery; diff --git a/queries/futures/useGetFuturesCumulativeStats.ts b/queries/futures/useGetFuturesCumulativeStats.ts index 94537d7ac1..cd8dbae8fa 100644 --- a/queries/futures/useGetFuturesCumulativeStats.ts +++ b/queries/futures/useGetFuturesCumulativeStats.ts @@ -1,4 +1,3 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; import { wei } from '@synthetixio/wei'; import request, { gql } from 'graphql-request'; import { useQuery, UseQueryOptions } from 'react-query'; @@ -8,6 +7,7 @@ import ROUTES from 'constants/routes'; import Connector from 'containers/Connector'; import useIsL2 from 'hooks/useIsL2'; import { FUTURES_ENDPOINT_OP_MAINNET } from 'sdk/constants/futures'; +import { NetworkId } from 'sdk/types/common'; import { getFuturesEndpoint } from 'sdk/utils/futures'; import logError from 'utils/logError'; diff --git a/queries/futures/useGetFuturesTradersStats.ts b/queries/futures/useGetFuturesTradersStats.ts new file mode 100644 index 0000000000..5174b5a4dc --- /dev/null +++ b/queries/futures/useGetFuturesTradersStats.ts @@ -0,0 +1,84 @@ +import { useQuery } from 'react-query'; + +import { chain } from 'containers/Connector/config'; +import { NetworkId } from 'sdk/types/common'; +import { getFuturesEndpoint } from 'sdk/utils/futures'; +import { useAppSelector } from 'state/hooks'; +import { selectMinTimestamp } from 'state/stats/selectors'; +import logError from 'utils/logError'; + +import { getFuturesPositions } from './subgraph'; + +type TradersStatMap = Record< + string, + { + [account: string]: number; + } +>; + +type TradersStat = { + date: string; + uniqueTradersByPeriod: number; + totalUniqueTraders: number; +}; + +export const useGetFuturesTradersStats = () => { + const minTimestamp = useAppSelector(selectMinTimestamp); + + const query = async () => { + try { + const futuresEndpoint = getFuturesEndpoint(chain.optimism.id as NetworkId); + const response = await getFuturesPositions( + futuresEndpoint, + { + first: 99999, + orderDirection: 'asc', + orderBy: 'openTimestamp', + where: { + openTimestamp_gt: minTimestamp, + }, + }, + { + openTimestamp: true, + account: true, + } + ); + + const summary = response.reduce((acc: TradersStatMap, res) => { + const date = new Date(Number(res.openTimestamp) * 1000).toISOString().split('T')[0]; + + if (!acc[date]) acc[date] = {}; + if (!acc[date][res.account]) acc[date][res.account] = 0; + acc[date][res.account] += 1; + return acc; + }, {}); + + let cumulativeAccounts = new Set(); + const result = Object.entries(summary) + .sort((a, b) => (new Date(a[0]) > new Date(b[0]) ? 1 : -1)) + .map(([date, accounts]) => { + const uniqueAccounts = Object.keys(accounts); + + // set cumulative account set + cumulativeAccounts = new Set([...cumulativeAccounts, ...uniqueAccounts]); + + // set values + return { + date, + uniqueTradersByPeriod: uniqueAccounts.length, + totalUniqueTraders: cumulativeAccounts.size, + }; + }); + + return result; + } catch (e) { + logError(e); + return []; + } + }; + + return useQuery({ + queryKey: ['Stats', 'Traders', minTimestamp], + queryFn: query, + }); +}; diff --git a/queries/futures/useGetFuturesTrades.ts b/queries/futures/useGetFuturesTrades.ts index 531d0959c2..d1d64b89fa 100644 --- a/queries/futures/useGetFuturesTrades.ts +++ b/queries/futures/useGetFuturesTrades.ts @@ -1,4 +1,3 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; import { utils as ethersUtils } from 'ethers'; import { useInfiniteQuery, UseInfiniteQueryOptions } from 'react-query'; @@ -6,6 +5,7 @@ import { DEFAULT_NUMBER_OF_TRADES, MAX_TIMESTAMP } from 'constants/defaults'; import QUERY_KEYS from 'constants/queryKeys'; import Connector from 'containers/Connector'; import { notNill } from 'queries/synths/utils'; +import { NetworkId } from 'sdk/types/common'; import { FuturesTrade } from 'sdk/types/futures'; import { getFuturesEndpoint, mapTrades } from 'sdk/utils/futures'; import logError from 'utils/logError'; @@ -53,7 +53,10 @@ const useGetFuturesTrades = ( positionClosed: true, pnl: true, feesPaid: true, + keeperFeesPaid: true, orderType: true, + fundingAccrued: true, + trackingCode: true, } ); return response ? mapTrades(response) : null; @@ -65,7 +68,7 @@ const useGetFuturesTrades = ( { ...options, refetchInterval: 15000, - getNextPageParam: (lastPage, _) => { + getNextPageParam: (lastPage) => { return notNill(lastPage) && lastPage?.length > 0 ? { minTs: 0, @@ -73,7 +76,7 @@ const useGetFuturesTrades = ( } : null; }, - getPreviousPageParam: (firstPage, _) => { + getPreviousPageParam: (firstPage) => { return notNill(firstPage) && firstPage?.length > 0 ? { minTs: firstPage[0].timestamp.toNumber(), diff --git a/queries/futures/useGetStats.ts b/queries/futures/useGetStats.ts index 0cfcf02ca3..4427129179 100644 --- a/queries/futures/useGetStats.ts +++ b/queries/futures/useGetStats.ts @@ -1,4 +1,3 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; import { useQuery, UseQueryOptions } from 'react-query'; import { ETH_UNIT } from 'constants/network'; @@ -6,6 +5,7 @@ import QUERY_KEYS from 'constants/queryKeys'; import Connector from 'containers/Connector'; import useIsL2 from 'hooks/useIsL2'; import { FUTURES_ENDPOINT_OP_MAINNET } from 'sdk/constants/futures'; +import { NetworkId } from 'sdk/types/common'; import { getFuturesEndpoint } from 'sdk/utils/futures'; import { truncateAddress } from 'utils/formatters/string'; import logError from 'utils/logError'; diff --git a/queries/futures/useGetStatsVolumes.ts b/queries/futures/useGetStatsVolumes.ts new file mode 100644 index 0000000000..f0c3c52e9a --- /dev/null +++ b/queries/futures/useGetStatsVolumes.ts @@ -0,0 +1,72 @@ +import { wei } from '@synthetixio/wei'; +import { useQuery } from 'react-query'; + +import { chain } from 'containers/Connector/config'; +import { PERIOD_IN_SECONDS } from 'sdk/constants/period'; +import { NetworkId } from 'sdk/types/common'; +import { getFuturesEndpoint } from 'sdk/utils/futures'; +import { useAppSelector } from 'state/hooks'; +import { selectMinTimestamp } from 'state/stats/selectors'; +import { weiFromWei } from 'utils/formatters/number'; +import logError from 'utils/logError'; + +import { AGGREGATE_ASSET_KEY } from './constants'; +import { getFuturesAggregateStats } from './subgraph'; + +type VolumeStat = { + date: string; + trades: number; + volume: number; + cumulativeTrades?: number; +}; + +export const useGetStatsVolumes = () => { + const futuresEndpoint = getFuturesEndpoint(chain.optimism.id as NetworkId); + const minTimestamp = useAppSelector(selectMinTimestamp); + + const query = async () => { + try { + const response = await getFuturesAggregateStats( + futuresEndpoint, + { + first: 999999, + orderBy: 'timestamp', + orderDirection: 'asc', + where: { + period: `${PERIOD_IN_SECONDS.ONE_DAY}`, + timestamp_gt: minTimestamp, + asset: AGGREGATE_ASSET_KEY, + }, + }, + { + asset: true, + timestamp: true, + trades: true, + volume: true, + } + ); + + let cumulativeTrades = wei(0); + const result: VolumeStat[] = response.map(({ timestamp, trades, volume }) => { + cumulativeTrades = cumulativeTrades.add(trades); + const thisTimestamp = timestamp.mul(1000).toNumber(); + const date = new Date(thisTimestamp).toISOString().split('T')[0]; + return { + date, + trades: trades.toNumber(), + volume: weiFromWei(volume ?? 0).toNumber(), + cumulativeTrades: cumulativeTrades.toNumber(), + }; + }); + return result; + } catch (e) { + logError(e); + return []; + } + }; + + return useQuery({ + queryKey: ['Stats', 'Volumes', minTimestamp], + queryFn: query, + }); +}; diff --git a/queries/futures/useLeaderboard.ts b/queries/futures/useLeaderboard.ts index 36b37fb1c2..66346361af 100644 --- a/queries/futures/useLeaderboard.ts +++ b/queries/futures/useLeaderboard.ts @@ -1,10 +1,10 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; import { wei } from '@synthetixio/wei'; import request, { gql } from 'graphql-request'; import { useQuery, UseQueryOptions } from 'react-query'; import QUERY_KEYS from 'constants/queryKeys'; import Connector from 'containers/Connector'; +import { NetworkId } from 'sdk/types/common'; import { getFuturesEndpoint } from 'sdk/utils/futures'; import { weiFromWei } from 'utils/formatters/number'; import { truncateAddress } from 'utils/formatters/string'; diff --git a/queries/futures/utils.ts b/queries/futures/utils.ts index f69c731c36..b25ab8a32f 100644 --- a/queries/futures/utils.ts +++ b/queries/futures/utils.ts @@ -1,70 +1,7 @@ -import { BigNumber } from '@ethersproject/bignumber'; -import { ContractsMap } from '@synthetixio/contracts-interface'; import { wei } from '@synthetixio/wei'; -import { ETH_UNIT } from 'constants/network'; -import { MarketClosureReason } from 'hooks/useMarketClosed'; import { SynthsTrades, SynthsVolumes } from 'queries/synths/type'; -import { FuturesOpenInterest, FuturesOneMinuteStat } from './types'; - -export const getFuturesMarketContract = (asset: string | null, contracts: ContractsMap) => { - if (!asset) throw new Error(`Asset needs to be specified`); - const contractName = `FuturesMarket${asset[0] === 's' ? asset.substring(1) : asset}`; - const contract = contracts[contractName]; - if (!contract) throw new Error(`${contractName} for ${asset} does not exist`); - return contract; -}; - -type MarketSizes = { - short: BigNumber; - long: BigNumber; -}; - -export const mapOpenInterest = async ( - keys: string[], - contracts: ContractsMap -): Promise => { - const openInterest: FuturesOpenInterest[] = []; - for (const key of keys) { - const contract = contracts[`FuturesMarket${key.substr(1)}`]; - if (contract) { - const marketSizes: MarketSizes = await contract.marketSizes(); - const shortSize = wei(marketSizes.short); - const longSize = wei(marketSizes.long); - - if (shortSize.toNumber() === 0 && longSize.toNumber() === 0) { - openInterest.push({ - asset: key, - ratio: { - long: 0.5, - short: 0.5, - }, - }); - } else if (shortSize.toNumber() === 0 || longSize.toNumber() === 0) { - openInterest.push({ - asset: key, - ratio: { - long: shortSize.toNumber() === 0 ? 1 : 0, - short: longSize.toNumber() === 0 ? 1 : 0, - }, - }); - } else { - const combined = shortSize.add(longSize); - - openInterest.push({ - asset: key, - ratio: { - long: longSize.div(combined).toNumber(), - short: shortSize.div(combined).toNumber(), - }, - }); - } - } - } - return openInterest; -}; - export const calculateTradeVolumeForAllSynths = (SynthTrades: SynthsTrades): SynthsVolumes => { const synthVolumes: SynthsVolumes = {}; const result = SynthTrades.synthExchanges @@ -79,38 +16,3 @@ export const calculateTradeVolumeForAllSynths = (SynthTrades: SynthsTrades): Syn }, synthVolumes); return result; }; - -export const calculateDailyTradeStats = (futuresTrades: FuturesOneMinuteStat[]) => { - return futuresTrades.reduce( - (acc, stat) => { - return { - totalVolume: acc.totalVolume.add(stat.volume.div(ETH_UNIT).abs()), - totalTrades: acc.totalTrades + Number(stat.trades), - }; - }, - { - totalTrades: 0, - totalVolume: wei(0), - } - ); -}; - -export const getReasonFromCode = ( - reasonCode?: BigNumber -): MarketClosureReason | 'unknown' | null => { - switch (Number(reasonCode)) { - case 1: - return 'system-upgrade'; - case 2: - return 'market-closure'; - case 3: - case 55: - case 65: - case 231: - return 'circuit-breaker'; - case 99999: - return 'emergency'; - default: - return 'unknown'; - } -}; diff --git a/queries/rates/constants.ts b/queries/rates/constants.ts index e6dede9974..149c90c6d6 100644 --- a/queries/rates/constants.ts +++ b/queries/rates/constants.ts @@ -1,7 +1,5 @@ import { chain } from 'containers/Connector/config'; -export const RATES_ENDPOINT_MAIN = `https://gateway.thegraph.com/api/${process.env.NEXT_PUBLIC_THEGRAPH_API_KEY}/subgraphs/id/HLy7PdmPJuVGjjmPNz1vW5RCCRpqzRWony2fSn7UKpf9`; - export const RATES_ENDPOINT_OP_MAINNET = `https://subgraph.satsuma-prod.com/${process.env.NEXT_PUBLIC_SATSUMA_API_KEY}/kwenta/optimism-latest-rates/api`; export const RATES_ENDPOINT_OP_GOERLI = @@ -19,7 +17,6 @@ export const COMMODITIES_BASE_API_URL = export const FOREX_BASE_API_URL = 'https://api.exchangerate.host/latest'; export const RATES_ENDPOINTS = { - [chain.mainnet.id]: RATES_ENDPOINT_MAIN, [chain.goerli.id]: RATES_ENDPOINT_GOERLI, [chain.optimism.id]: RATES_ENDPOINT_OP_MAINNET, [chain.optimismGoerli.id]: RATES_ENDPOINT_OP_GOERLI, diff --git a/queries/rates/useCandlesticksQuery.ts b/queries/rates/useCandlesticksQuery.ts index f97a11b69a..14792d1082 100644 --- a/queries/rates/useCandlesticksQuery.ts +++ b/queries/rates/useCandlesticksQuery.ts @@ -1,6 +1,5 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; - import { getCandles } from 'queries/futures/subgraph'; +import { NetworkId } from 'sdk/types/common'; import { getRatesEndpoint, mapCandles, mapPriceChart } from './utils'; diff --git a/queries/rates/useRateUpdateQuery.ts b/queries/rates/useRateUpdateQuery.ts index ad31193493..270f7e9b07 100644 --- a/queries/rates/useRateUpdateQuery.ts +++ b/queries/rates/useRateUpdateQuery.ts @@ -1,4 +1,3 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; import request, { gql } from 'graphql-request'; import { useQuery, UseQueryOptions } from 'react-query'; @@ -6,6 +5,7 @@ import QUERY_KEYS from 'constants/queryKeys'; import Connector from 'containers/Connector'; import { chain } from 'containers/Connector/config'; import useIsL2 from 'hooks/useIsL2'; +import { NetworkId } from 'sdk/types/common'; import logError from 'utils/logError'; import { getRatesEndpoint } from './utils'; diff --git a/queries/rates/utils.ts b/queries/rates/utils.ts index de5e75a035..f9fd0ab5c4 100644 --- a/queries/rates/utils.ts +++ b/queries/rates/utils.ts @@ -1,7 +1,6 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; - import { chain } from 'containers/Connector/config'; import { CandleResult } from 'queries/futures/subgraph'; +import { NetworkId } from 'sdk/types/common'; import { FuturesMarketKey } from 'sdk/types/futures'; import { RATES_ENDPOINTS } from './constants'; diff --git a/queries/staking/useGetFuturesFeeForAccount.ts b/queries/staking/useGetFuturesFeeForAccount.ts index 4715fae496..fd8e60c099 100644 --- a/queries/staking/useGetFuturesFeeForAccount.ts +++ b/queries/staking/useGetFuturesFeeForAccount.ts @@ -35,6 +35,7 @@ const useGetFuturesFeeForAccount = ( abstractAccount: true, accountType: true, feesPaid: true, + keeperFeesPaid: true, } ); return response; diff --git a/queries/staking/utils.ts b/queries/staking/utils.ts index be5b0353fe..d1b48a001a 100644 --- a/queries/staking/utils.ts +++ b/queries/staking/utils.ts @@ -1,7 +1,7 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; import { wei } from '@synthetixio/wei'; import { BigNumber } from 'ethers'; +import { NetworkId } from 'sdk/types/common'; import { formatShortDate, toJSTimestamp } from 'utils/formatters/date'; import { zeroBN } from 'utils/formatters/number'; @@ -29,6 +29,7 @@ export type FuturesFeeForAccountProps = { abstractAccount: string; accountType: string; feesPaid: BigNumber; + keeperFeesPaid: BigNumber; }; export type FuturesFeeProps = { diff --git a/queries/synths/type.ts b/queries/synths/type.ts index ef42b8a80c..b0344b2ad2 100644 --- a/queries/synths/type.ts +++ b/queries/synths/type.ts @@ -1,6 +1,12 @@ -import { SynthResult } from '@synthetixio/queries'; import Wei from '@synthetixio/wei'; +type SynthResult = { + id: string; + name: string; + symbol: string; + totalSupply: Wei; +}; + export type SynthsVolumes = { [asset: string]: Wei; }; diff --git a/queries/synths/useGetSynthsTradingVolumeForAllMarkets.ts b/queries/synths/useGetSynthsTradingVolumeForAllMarkets.ts index 47c021bb4c..b9a6501341 100644 --- a/queries/synths/useGetSynthsTradingVolumeForAllMarkets.ts +++ b/queries/synths/useGetSynthsTradingVolumeForAllMarkets.ts @@ -1,4 +1,3 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; import request, { gql } from 'graphql-request'; import { useQuery } from 'react-query'; @@ -8,6 +7,7 @@ import Connector from 'containers/Connector'; import { chain } from 'containers/Connector/config'; import { calculateTradeVolumeForAllSynths } from 'queries/futures/utils'; import { MAIN_ENDPOINT_OP_MAINNET } from 'sdk/constants/futures'; +import { NetworkId } from 'sdk/types/common'; import { getMainEndpoint } from 'sdk/utils/futures'; import logError from 'utils/logError'; diff --git a/queries/synths/useGetWalletTrades.ts b/queries/synths/useGetWalletTrades.ts index 3b6da41e68..b97f227b04 100644 --- a/queries/synths/useGetWalletTrades.ts +++ b/queries/synths/useGetWalletTrades.ts @@ -1,9 +1,9 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; import request, { gql } from 'graphql-request'; import { useQuery, UseQueryOptions } from 'react-query'; import QUERY_KEYS from 'constants/queryKeys'; import Connector from 'containers/Connector'; +import { NetworkId } from 'sdk/types/common'; import { getMainEndpoint } from 'sdk/utils/futures'; import logError from 'utils/logError'; diff --git a/sdk/common/gas.ts b/sdk/common/gas.ts index 5475ea35e5..3e85f6bf07 100644 --- a/sdk/common/gas.ts +++ b/sdk/common/gas.ts @@ -2,11 +2,11 @@ // https://github.com/Synthetixio/js-monorepo/blob/master/packages/queries/src/queries/network/useEthGasPriceQuery.ts import { getContractFactory, predeploys } from '@eth-optimism/contracts'; -import { NetworkId, NetworkIdByName } from '@synthetixio/contracts-interface'; import { wei } from '@synthetixio/wei'; import { ethers, BigNumber } from 'ethers'; import { omit } from 'lodash'; +import { NetworkId, NetworkIdByName } from 'sdk/types/common'; import { weiFromWei, zeroBN } from 'utils/formatters/number'; const MULTIPLIER = wei(2); @@ -85,13 +85,10 @@ export const getEthGasPrice = async (networkId: NetworkId, provider: ethers.prov fast: computeGasFee(block.baseFeePerGas, 4), average: computeGasFee(block.baseFeePerGas, 2), }; - } else { - return getGasPriceFromProvider(provider); } - // If not (Testnet or Optimism network), we get the Gas Price through the provider - } else { - return getGasPriceFromProvider(provider); } + + return getGasPriceFromProvider(provider); } catch (e) { throw new Error(`Could not fetch and compute network fee. ${e}`); } diff --git a/sdk/constants/futures.ts b/sdk/constants/futures.ts index 4edfa39ed5..661e798da7 100644 --- a/sdk/constants/futures.ts +++ b/sdk/constants/futures.ts @@ -5,6 +5,8 @@ import { FuturesMarketAsset, FuturesMarketConfig, FuturesMarketKey } from 'sdk/t export const KWENTA_TRACKING_CODE = formatBytes32String('KWENTA'); +export const DEFAULT_NUMBER_OF_TRADES = 16; + export const FUTURES_ENDPOINT_OP_MAINNET = `https://subgraph.satsuma-prod.com/${process.env.NEXT_PUBLIC_SATSUMA_API_KEY}/kwenta/optimism-perps/api`; export const FUTURES_ENDPOINT_OP_GOERLI = diff --git a/sdk/context.ts b/sdk/context.ts index 12c331edb7..4c34d1fc58 100644 --- a/sdk/context.ts +++ b/sdk/context.ts @@ -1,9 +1,10 @@ import { EventEmitter } from 'events'; -import { NetworkId } from '@synthetixio/contracts-interface'; import { Provider as EthCallProvider } from 'ethcall'; import { ethers } from 'ethers'; +import { NetworkId } from 'sdk/types/common'; + import * as sdkErrors from './common/errors'; import { ContractsMap, diff --git a/sdk/contracts/abis/DappMaintenance.json b/sdk/contracts/abis/DappMaintenance.json new file mode 100644 index 0000000000..073cc0ad44 --- /dev/null +++ b/sdk/contracts/abis/DappMaintenance.json @@ -0,0 +1,201 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldOwner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnerChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnerNominated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "isPaused", + "type": "bool" + } + ], + "name": "SXMaintenance", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "isPaused", + "type": "bool" + } + ], + "name": "StakingMaintenance", + "type": "event" + }, + { + "constant": false, + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isPausedSX", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isPausedStaking", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "nominateNewOwner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "nominatedOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bool", + "name": "isPaused", + "type": "bool" + } + ], + "name": "setMaintenanceModeAll", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bool", + "name": "isPaused", + "type": "bool" + } + ], + "name": "setMaintenanceModeSX", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bool", + "name": "isPaused", + "type": "bool" + } + ], + "name": "setMaintenanceModeStaking", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/sdk/contracts/constants.ts b/sdk/contracts/constants.ts index 9c6c73e9cb..5d99e294ec 100644 --- a/sdk/contracts/constants.ts +++ b/sdk/contracts/constants.ts @@ -125,4 +125,8 @@ export const ADDRESSES: Record> = { BatchClaimer: { 10: '0x6Fd879830D9b1EE5d4f9ef12f8D5deE916bebD0b', }, + DappMaintenance: { + 10: '0x54581A23F62D147AC76d454f0b3eF77F9D766058', + 420: '0x4259a2004A1E110A86564ff1441c37F1461F344F', + }, }; diff --git a/sdk/contracts/index.ts b/sdk/contracts/index.ts index 79cb5f9700..cb7480354b 100644 --- a/sdk/contracts/index.ts +++ b/sdk/contracts/index.ts @@ -1,13 +1,15 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; import { Contract as EthCallContract } from 'ethcall'; import { Contract, ethers } from 'ethers'; +import { NetworkId } from 'sdk/types/common'; + import ERC20ABI from '../contracts/abis/ERC20.json'; import MultipleMerkleDistributorABI from '../contracts/abis/MultipleMerkleDistributor.json'; import MultipleMerkleDistributorPerpsV2ABI from '../contracts/abis/MultipleMerkleDistributorPerpsV2.json'; import RewardEscrowABI from '../contracts/abis/RewardEscrow.json'; import SupplyScheduleABI from '../contracts/abis/SupplySchedule.json'; import CrossMarginBaseSettingsABI from './abis/CrossMarginBaseSettings.json'; +import DappMaintenanceABI from './abis/DappMaintenance.json'; import ExchangeRatesABI from './abis/ExchangeRates.json'; import FuturesMarketDataABI from './abis/FuturesMarketData.json'; import FuturesMarketSettingsABI from './abis/FuturesMarketSettings.json'; @@ -18,6 +20,7 @@ import PerpsV2MarketDataABI from './abis/PerpsV2MarketData.json'; import PerpsV2MarketSettingsABI from './abis/PerpsV2MarketSettings.json'; import StakingRewardsABI from './abis/StakingRewards.json'; import SynthRedeemerABI from './abis/SynthRedeemer.json'; +import SystemStatusABI from './abis/SystemStatus.json'; import { ADDRESSES } from './constants'; import { CrossMarginAccountFactory__factory, @@ -223,6 +226,12 @@ export const getMulticallContractsByNetwork = (networkId: NetworkId) => { SupplySchedule: ADDRESSES.SupplySchedule[networkId] ? new EthCallContract(ADDRESSES.SupplySchedule[networkId], SupplyScheduleABI) : undefined, + SystemStatus: ADDRESSES.SystemStatus[networkId] + ? new EthCallContract(ADDRESSES.SystemStatus[networkId], SystemStatusABI) + : undefined, + DappMaintenance: ADDRESSES.DappMaintenance[networkId] + ? new EthCallContract(ADDRESSES.DappMaintenance[networkId], DappMaintenanceABI) + : undefined, }; }; diff --git a/sdk/data/synths.ts b/sdk/data/synths.ts index b5b9b17ceb..e1befcab3f 100644 --- a/sdk/data/synths.ts +++ b/sdk/data/synths.ts @@ -1,4 +1,4 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; +import { NetworkId } from 'sdk/types/common'; export type SynthSymbol = | 'sAAVE' diff --git a/sdk/index.ts b/sdk/index.ts index d031eaf595..4777067b25 100644 --- a/sdk/index.ts +++ b/sdk/index.ts @@ -6,6 +6,7 @@ import FuturesService from './services/futures'; import KwentaTokenService from './services/kwentaToken'; import PricesService from './services/prices'; import SynthsService from './services/synths'; +import SystemService from './services/system'; import TransactionsService from './services/transactions'; export default class KwentaSDK { @@ -17,6 +18,7 @@ export default class KwentaSDK { public transactions: TransactionsService; public kwentaToken: KwentaTokenService; public prices: PricesService; + public system: SystemService; constructor(context: IContext) { this.context = new Context(context); @@ -26,6 +28,7 @@ export default class KwentaSDK { this.synths = new SynthsService(this); this.transactions = new TransactionsService(this); this.kwentaToken = new KwentaTokenService(this); + this.system = new SystemService(this); } public setProvider(provider: ethers.providers.Provider) { diff --git a/sdk/queries/exchange.ts b/sdk/queries/exchange.ts new file mode 100644 index 0000000000..8cdc4e6175 --- /dev/null +++ b/sdk/queries/exchange.ts @@ -0,0 +1,19 @@ +import request, { gql } from 'graphql-request'; +import KwentaSDK from 'sdk'; + +export const queryPriceAdjustmentData = async (sdk: KwentaSDK, walletAddress: string) => { + const response = await request( + '', + gql` + query priceAdjustmentFee($walletAddress: String!) { + exchangeEntrySettleds(where: { from: $walletAddress }) { + rebate + reclaim + } + } + `, + { walletAddress } + ); + + return response?.exchangeEntrySettleds[0]; +}; diff --git a/sdk/queries/futures.ts b/sdk/queries/futures.ts index 19c00e3079..4965bd019e 100644 --- a/sdk/queries/futures.ts +++ b/sdk/queries/futures.ts @@ -7,7 +7,12 @@ import { getFuturesPositions, getFuturesTrades, } from 'queries/futures/subgraph'; -import { CROSS_MARGIN_FRAGMENT, ISOLATED_MARGIN_FRAGMENT } from 'sdk/constants/futures'; +import { + CROSS_MARGIN_FRAGMENT, + ISOLATED_MARGIN_FRAGMENT, + DEFAULT_NUMBER_OF_TRADES, +} from 'sdk/constants/futures'; +import { FuturesMarketKey } from 'sdk/types/futures'; import { mapCrossMarginTransfers, mapMarginTransfers } from 'sdk/utils/futures'; export const queryAccountsFromSubgraph = async ( @@ -91,7 +96,10 @@ export const queryTrades = async ( positionClosed: true, pnl: true, feesPaid: true, + keeperFeesPaid: true, orderType: true, + trackingCode: true, + fundingAccrued: true, } ); }; @@ -154,3 +162,45 @@ export const queryCrossMarginTransfers = async (sdk: KwentaSDK, account: string) }); return response ? mapCrossMarginTransfers(response.crossMarginAccountTransfers) : []; }; + +export const queryFuturesTrades = ( + sdk: KwentaSDK, + marketKey: FuturesMarketKey, + minTs: number, + maxTs: number +) => { + return getFuturesTrades( + sdk.futures.futuresGqlEndpoint, + { + first: DEFAULT_NUMBER_OF_TRADES, + where: { + marketKey: formatBytes32String(marketKey), + timestamp_gt: minTs, + timestamp_lt: maxTs, + }, + orderDirection: 'desc', + orderBy: 'timestamp', + }, + { + id: true, + timestamp: true, + account: true, + abstractAccount: true, + accountType: true, + margin: true, + size: true, + asset: true, + marketKey: true, + price: true, + positionId: true, + positionSize: true, + positionClosed: true, + pnl: true, + feesPaid: true, + keeperFeesPaid: true, + orderType: true, + fundingAccrued: true, + trackingCode: true, + } + ); +}; diff --git a/sdk/services/exchange.ts b/sdk/services/exchange.ts index 0f260f1da8..c9e4d67546 100644 --- a/sdk/services/exchange.ts +++ b/sdk/services/exchange.ts @@ -1,15 +1,15 @@ // @ts-ignore TODO: remove once types are added import getFormattedSwapData from '@kwenta/synthswap'; -import { CurrencyKey, NetworkId } from '@synthetixio/contracts-interface'; -import { DeprecatedSynthBalance } from '@synthetixio/queries'; import Wei, { wei } from '@synthetixio/wei'; import axios from 'axios'; import { Contract as EthCallContract } from 'ethcall'; import { BigNumber, ethers } from 'ethers'; +import { formatBytes32String } from 'ethers/lib/utils.js'; import { get, keyBy } from 'lodash'; import KwentaSDK from 'sdk'; import { KWENTA_REFERRAL_ADDRESS, SYNTH_SWAP_OPTIMISM_ADDRESS } from 'constants/address'; +import { CurrencyKey } from 'constants/currency'; import { ATOMIC_EXCHANGES_L1, CRYPTO_CURRENCY_MAP, @@ -24,17 +24,22 @@ import { getProxySynthSymbol } from 'queries/synths/utils'; import { getEthGasPrice } from 'sdk/common/gas'; import { KWENTA_TRACKING_CODE } from 'sdk/constants/futures'; import erc20Abi from 'sdk/contracts/abis/ERC20.json'; +import { NetworkId } from 'sdk/types/common'; +import { SynthSuspensionReason } from 'sdk/types/futures'; +import { DeprecatedSynthBalance } from 'sdk/types/synths'; import { Token, TokenBalances } from 'sdk/types/tokens'; +import { getReasonFromCode } from 'sdk/utils/synths'; import { newGetCoinGeckoPricesForCurrencies, - newGetExchangeRatesForCurrencies, - newGetExchangeRatesTupleForCurrencies, + getExchangeRatesForCurrencies, + getExchangeRatesTupleForCurrencies, } from 'utils/currencies'; import { UNIT_BIG_NUM, zeroBN } from 'utils/formatters/number'; import { getTransactionPrice, normalizeGasLimit } from 'utils/network'; import * as sdkErrors from '../common/errors'; import { getSynthsForNetwork, SynthSymbol } from '../data/synths'; +import { queryPriceAdjustmentData } from '../queries/exchange'; import { OneInchApproveSpenderResponse, OneInchQuoteResponse, @@ -510,7 +515,7 @@ export default class ExchangeService { ) { const gasPrices = await getEthGasPrice(this.sdk.context.networkId, this.sdk.context.provider); const txProvider = this.getTxProvider(baseCurrencyKey, quoteCurrencyKey); - const ethPriceRate = newGetExchangeRatesForCurrencies(this.exchangeRates, 'sETH', 'sUSD'); + const ethPriceRate = getExchangeRatesForCurrencies(this.exchangeRates, 'sETH', 'sUSD'); const gasPrice = gasPrices.fast; if (txProvider === 'synthswap' || txProvider === '1inch') { @@ -676,7 +681,7 @@ export default class ExchangeService { } else { return this.checkIsAtomic(currencyKey, 'sUSD') ? await this.getAtomicRates(currencyKey) - : newGetExchangeRatesForCurrencies(this.exchangeRates, currencyKey, 'sUSD'); + : getExchangeRatesForCurrencies(this.exchangeRates, currencyKey, 'sUSD'); } } @@ -807,6 +812,57 @@ export default class ExchangeService { return { tokensMap: this.tokensMap, tokenList: this.tokenList }; } + public async getPriceAdjustment() { + const { rebate, reclaim } = await queryPriceAdjustmentData( + this.sdk, + this.sdk.context.walletAddress + ); + + return { rebate: wei(rebate), reclaim: wei(reclaim) }; + } + + public async getSynthSuspensions() { + const { SystemStatus } = this.sdk.context.multicallContracts; + + const synthsMap = this.sdk.exchange.getSynthsMap(); + + if (!SystemStatus) { + throw new Error(sdkErrors.UNSUPPORTED_NETWORK); + } + + const calls = []; + + for (let synth in synthsMap) { + calls.push(SystemStatus.synthExchangeSuspension(formatBytes32String(synth))); + } + + const responses = (await this.sdk.context.multicallProvider.all(calls)) as [ + boolean, + BigNumber + ][]; + + let ret: Record< + string, + { isSuspended: boolean; reasonCode: number; reason: SynthSuspensionReason | null } + > = {}; + let i = 0; + + for (let synth in synthsMap) { + const [isSuspended, reason] = responses[i]; + const reasonCode = Number(reason); + + ret[synth] = { + isSuspended: responses[i][0], + reasonCode, + reason: isSuspended ? getReasonFromCode(reasonCode) : null, + }; + + i++; + } + + return ret; + } + private checkIsAtomic(baseCurrencyKey: string, quoteCurrencyKey: string) { if (this.sdk.context.isL2 || !baseCurrencyKey || !quoteCurrencyKey) { return false; @@ -914,7 +970,7 @@ export default class ExchangeService { this.getAtomicRates(quoteCurrencyKey), this.getAtomicRates(baseCurrencyKey), ]) - : newGetExchangeRatesTupleForCurrencies( + : getExchangeRatesTupleForCurrencies( this.sdk.prices.currentPrices.onChain, quoteCurrencyKey, baseCurrencyKey diff --git a/sdk/services/futures.ts b/sdk/services/futures.ts index 61f2d45a1f..d1d3ede31a 100644 --- a/sdk/services/futures.ts +++ b/sdk/services/futures.ts @@ -1,5 +1,3 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; -import { PERIOD_IN_HOURS } from '@synthetixio/queries'; import Wei, { wei } from '@synthetixio/wei'; import { Contract as EthCallContract } from 'ethcall'; import { BigNumber, ethers } from 'ethers'; @@ -9,14 +7,13 @@ import { orderBy } from 'lodash'; import KwentaSDK from 'sdk'; import { getFuturesAggregateStats } from 'queries/futures/subgraph'; -import { FuturesAccountType } from 'queries/futures/types'; import { UNSUPPORTED_NETWORK } from 'sdk/common/errors'; import { BPS_CONVERSION, DEFAULT_DESIRED_TIMEDELTA, KWENTA_TRACKING_CODE, } from 'sdk/constants/futures'; -import { Period, PERIOD_IN_SECONDS } from 'sdk/constants/period'; +import { Period, PERIOD_IN_HOURS, PERIOD_IN_SECONDS } from 'sdk/constants/period'; import { getContractsByNetwork, getPerpsV2MarketMulticall } from 'sdk/contracts'; import CrossMarginBaseABI from 'sdk/contracts/abis/CrossMarginBase.json'; import FuturesMarketABI from 'sdk/contracts/abis/FuturesMarket.json'; @@ -31,10 +28,12 @@ import { IPerpsV2MarketSettings } from 'sdk/contracts/types/PerpsV2MarketData'; import { queryCrossMarginAccounts, queryCrossMarginTransfers, + queryFuturesTrades, queryIsolatedMarginTransfers, queryPositionHistory, queryTrades, } from 'sdk/queries/futures'; +import { NetworkId } from 'sdk/types/common'; import { NetworkOverrideOptions } from 'sdk/types/common'; import { CrossMarginOrderType, @@ -51,6 +50,7 @@ import { PositionSide, ModifyPositionOptions, MarginTransfer, + FuturesAccountType, } from 'sdk/types/futures'; import { PricesMap } from 'sdk/types/prices'; import { @@ -61,13 +61,13 @@ import { formatPotentialTrade, getFuturesEndpoint, getMarketName, - getReasonFromCode, mapFuturesOrderFromEvent, mapFuturesPosition, mapFuturesPositions, mapTrades, marketsForNetwork, } from 'sdk/utils/futures'; +import { getReasonFromCode } from 'sdk/utils/synths'; import { calculateTimestampForPeriod } from 'utils/formatters/date'; import { MarketAssetByKey, MarketKeyByAsset } from 'utils/futures'; @@ -123,12 +123,14 @@ export default class FuturesService { PerpsV2MarketData.MarketSummaryStructOutput[] | PerpsV2MarketData.FuturesGlobalsStructOutput > = await this.sdk.context.multicallProvider.all([ PerpsV2MarketData.allProxiedMarketSummaries(), - PerpsV2MarketData.globals(), + PerpsV2MarketSettings.minInitialMargin(), + PerpsV2MarketSettings.minKeeperFee(), ]); - const { markets, globals } = { + const { markets, minInitialMargin, minKeeperFee } = { markets: futuresData[0] as PerpsV2MarketData.MarketSummaryStructOutput[], - globals: futuresData[1] as PerpsV2MarketData.FuturesGlobalsStructOutput, + minInitialMargin: futuresData[1] as PerpsV2MarketData.FuturesGlobalsStructOutput, + minKeeperFee: futuresData[2] as PerpsV2MarketData.FuturesGlobalsStructOutput, }; const filteredMarkets = markets.filter((m) => { @@ -213,8 +215,8 @@ export default class FuturesService { maxLeverage: wei(maxLeverage), marketSize: wei(marketSize), marketLimit: wei(marketParameters[i].maxMarketValue).mul(wei(price)), - minInitialMargin: wei(globals.minInitialMargin), - keeperDeposit: wei(globals.minKeeperFee), + minInitialMargin: wei(minInitialMargin), + keeperDeposit: wei(minKeeperFee), isSuspended: suspensions[i], marketClosureReason: getReasonFromCode(reasons[i]) as MarketClosureReason, settings: { @@ -613,6 +615,18 @@ export default class FuturesService { return response ? mapTrades(response) : []; } + // This is on an interval of 15 seconds. + public async getFuturesTrades(marketKey: FuturesMarketKey, minTs: number, maxTs: number) { + const response = await queryFuturesTrades(this.sdk, marketKey, minTs, maxTs); + return response ? mapTrades(response) : null; + } + + public async getOrderFee(marketAddress: string, size: Wei) { + const marketContract = PerpsV2Market__factory.connect(marketAddress, this.sdk.context.signer); + const orderFee = await marketContract.orderFee(size.toBN(), 0); + return wei(orderFee.fee); + } + // Contract mutations public async approveCrossMarginDeposit( diff --git a/sdk/services/prices.ts b/sdk/services/prices.ts index 4568219f08..ccc87a2522 100644 --- a/sdk/services/prices.ts +++ b/sdk/services/prices.ts @@ -1,5 +1,4 @@ import { EvmPriceServiceConnection, PriceFeed } from '@pythnetwork/pyth-evm-js'; -import { CurrencyKey, NetworkId } from '@synthetixio/contracts-interface'; import Wei, { wei } from '@synthetixio/wei'; import { formatEther, parseBytes32String } from 'ethers/lib/utils.js'; import request, { gql } from 'graphql-request'; @@ -9,6 +8,7 @@ import KwentaSDK from 'sdk'; import { MARKETS, MARKET_ASSETS_BY_PYTH_ID } from 'sdk/constants/futures'; import { PERIOD_IN_SECONDS } from 'sdk/constants/period'; import { ADDITIONAL_SYNTHS, PRICE_UPDATE_THROTTLE, PYTH_IDS } from 'sdk/constants/prices'; +import { NetworkId } from 'sdk/types/common'; import { FuturesMarketKey } from 'sdk/types/futures'; import { CurrencyPrice, @@ -126,11 +126,11 @@ export default class PricesService { this.sdk.context.contracts.ExchangeRates.ratesForCurrencies(ADDITIONAL_SYNTHS), ])) as [SynthPricesTuple, CurrencyPrice[]]; - const synths = [...synthsRates[0], ...ADDITIONAL_SYNTHS] as CurrencyKey[]; + const synths = [...synthsRates[0], ...ADDITIONAL_SYNTHS]; const rates = [...synthsRates[1], ...ratesForCurrencies] as CurrencyPrice[]; - synths.forEach((currencyKeyBytes32: CurrencyKey, idx: number) => { - const currencyKey = parseBytes32String(currencyKeyBytes32) as CurrencyKey; + synths.forEach((currencyKeyBytes32, idx: number) => { + const currencyKey = parseBytes32String(currencyKeyBytes32); const marketAsset = MarketAssetByKey[currencyKey as FuturesMarketKey]; const rate = Number(formatEther(rates[idx])); diff --git a/sdk/services/stats.ts b/sdk/services/stats.ts new file mode 100644 index 0000000000..2994a6e6d2 --- /dev/null +++ b/sdk/services/stats.ts @@ -0,0 +1,15 @@ +import KwentaSDK from 'sdk'; + +export default class StatsService { + private sdk: KwentaSDK; + + constructor(sdk: KwentaSDK) { + this.sdk = sdk; + } + + public async getStatsVolumes() {} + + public async getFuturesTradersStats() {} + + public async getFuturesCumulativeStats() {} +} diff --git a/sdk/services/synths.ts b/sdk/services/synths.ts index 8d98f7816c..dea1970159 100644 --- a/sdk/services/synths.ts +++ b/sdk/services/synths.ts @@ -1,4 +1,3 @@ -import { CurrencyKey } from '@synthetixio/contracts-interface'; import { wei } from '@synthetixio/wei'; import { ethers } from 'ethers'; import { orderBy } from 'lodash'; @@ -39,7 +38,7 @@ export default class SynthsService { const balance = wei(synthsBalances[idx]); if (balance.gt(0)) { - const synthName = ethers.utils.parseBytes32String(currencyKeyBytes32) as CurrencyKey; + const synthName = ethers.utils.parseBytes32String(currencyKeyBytes32); const usdBalance = wei(synthsUSDBalances[idx]); balancesMap[synthName] = { currencyKey: synthName, balance, usdBalance }; diff --git a/sdk/services/system.ts b/sdk/services/system.ts new file mode 100644 index 0000000000..63a8809199 --- /dev/null +++ b/sdk/services/system.ts @@ -0,0 +1,26 @@ +import KwentaSDK from 'sdk'; + +import { UNSUPPORTED_NETWORK } from 'sdk/common/errors'; + +export default class SystemService { + private sdk: KwentaSDK; + + constructor(sdk: KwentaSDK) { + this.sdk = sdk; + } + + public async getSynthetixStatus() { + const { SystemStatus, DappMaintenance } = this.sdk.context.multicallContracts; + + if (!SystemStatus || !DappMaintenance) { + throw new Error(UNSUPPORTED_NETWORK); + } + + const [isSystemUpgrading, isExchangePaused] = (await this.sdk.context.multicallProvider.all([ + SystemStatus.isSystemUpgrading(), + DappMaintenance.isPausedSX(), + ])) as [boolean, boolean]; + + return isSystemUpgrading || isExchangePaused; + } +} diff --git a/sdk/services/transactions.ts b/sdk/services/transactions.ts index ac7b14211f..b1271a6ae8 100644 --- a/sdk/services/transactions.ts +++ b/sdk/services/transactions.ts @@ -1,10 +1,12 @@ import { getContractFactory, predeploys } from '@eth-optimism/contracts'; -import { NetworkIdByName } from '@synthetixio/contracts-interface'; import { wei } from '@synthetixio/wei'; import { ethers } from 'ethers'; import { omit, clone } from 'lodash'; import KwentaSDK from 'sdk'; +import { getEthGasPrice } from 'sdk/common/gas'; +import { NetworkIdByName } from 'sdk/types/common'; + import * as sdkErrors from '../common/errors'; import { ContractName } from '../contracts'; @@ -105,4 +107,8 @@ export default class TransactionsService { return wei(await OptimismGasPriceOracleContract.getL1Fee(serializedTxn)); } + + public getGasPrice() { + return getEthGasPrice(this.sdk.context.networkId, this.sdk.context.provider); + } } diff --git a/sdk/types/1inch.ts b/sdk/types/1inch.ts index 8a8c74467d..96fbf60d35 100644 --- a/sdk/types/1inch.ts +++ b/sdk/types/1inch.ts @@ -1,7 +1,5 @@ -import { CurrencyKey } from '@synthetixio/contracts-interface'; - type Token = { - symbol: CurrencyKey; + symbol: string; name: string; address: string; decimals: number; diff --git a/sdk/types/common.ts b/sdk/types/common.ts index f3458b9bcf..fb5cd3a0a2 100644 --- a/sdk/types/common.ts +++ b/sdk/types/common.ts @@ -1,6 +1,7 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; import { providers } from 'ethers'; +export type NetworkId = 1 | 5 | 420 | 10 | 42 | 69 | 31337; + export type NetworkOverrideOptions = { networkId: NetworkId; provider: providers.Provider; @@ -12,3 +13,23 @@ export enum TransactionStatus { Confirmed = 'Confirmed', Failed = 'Failed', } + +export const NetworkIdByName = { + mainnet: 1, + goerli: 5, + 'goerli-ovm': 420, + 'mainnet-ovm': 10, + kovan: 42, + 'kovan-ovm': 69, + 'mainnet-fork': 31337, +} as const; + +export const NetworkNameById = { + 1: 'mainnet', + 5: 'goerli', + 42: 'kovan', + 10: 'mainnet-ovm', + 69: 'kovan-ovm', + 420: 'goerli-ovm', + 31337: 'mainnet-fork', +} as const; diff --git a/sdk/types/futures.ts b/sdk/types/futures.ts index 23bd7bd7c2..d836929b14 100644 --- a/sdk/types/futures.ts +++ b/sdk/types/futures.ts @@ -167,7 +167,7 @@ export enum PositionSide { SHORT = 'short', } -export type FuturesAccountType = 'cross_margin' | 'isolated_margin'; +export type FuturesAccountType = 'cross_margin' | 'smart_margin' | 'isolated_margin'; export enum ContractOrderType { MARKET = 0, @@ -354,6 +354,7 @@ export type FuturesTrade = { side: PositionSide; pnl: T; feesPaid: T; + keeperFeesPaid: T; orderType: FuturesOrderTypeDisplay; accountType: FuturesAccountType; }; diff --git a/sdk/types/synths.ts b/sdk/types/synths.ts new file mode 100644 index 0000000000..7d8a5c96fd --- /dev/null +++ b/sdk/types/synths.ts @@ -0,0 +1,46 @@ +import Wei from '@synthetixio/wei'; + +import { CurrencyKey } from 'constants/currency'; + +export type SynthBalance = { + currencyKey: CurrencyKey; + balance: Wei; + usdBalance: Wei; +}; + +export type SynthBalancesMap = Partial<{ [key: string]: SynthBalance }>; + +export type Balances = { + balancesMap: SynthBalancesMap; + balances: SynthBalance[]; + totalUSDBalance: Wei; +}; + +export type SynthResult = { + id: string; + name: string; + symbol: string; + totalSupply: Wei; +}; + +export type WalletTradesExchangeResult = { + id: string; + fromSynth: Partial | null; + toSynth: Partial | null; + fromAmount: Wei; + fromAmountInUSD: Wei; + toAmount: Wei; + toAmountInUSD: Wei; + feesInUSD: Wei; + toAddress: string; + timestamp: number; + gasPrice: Wei; + hash: string; +}; + +export type DeprecatedSynthBalance = { + currencyKey: CurrencyKey; + proxyAddress: string; + balance: Wei; + usdBalance: Wei; +}; diff --git a/sdk/types/tokens.ts b/sdk/types/tokens.ts index 5aff8bf4b5..0bf5f176be 100644 --- a/sdk/types/tokens.ts +++ b/sdk/types/tokens.ts @@ -1,6 +1,7 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; import Wei from '@synthetixio/wei'; +import { NetworkId } from 'sdk/types/common'; + export type SynthBalance = { currencyKey: string; balance: T; diff --git a/sdk/utils/futures.ts b/sdk/utils/futures.ts index 8f5cd4e33c..f7657a00df 100644 --- a/sdk/utils/futures.ts +++ b/sdk/utils/futures.ts @@ -1,4 +1,3 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; import Wei, { wei } from '@synthetixio/wei'; import { BigNumber } from 'ethers'; import { parseBytes32String } from 'ethers/lib/utils.js'; @@ -21,6 +20,7 @@ import { } from 'sdk/constants/futures'; import { SECONDS_PER_DAY } from 'sdk/constants/period'; import { IPerpsV2MarketConsolidated } from 'sdk/contracts/types/PerpsV2Market'; +import { NetworkId } from 'sdk/types/common'; import { DelayedOrder, CrossMarginOrderType, @@ -36,7 +36,6 @@ import { FuturesTrade, FuturesVolumes, IsolatedMarginOrderType, - MarketClosureReason, PositionDetail, PositionSide, PostTradeDetailsResponse, @@ -122,26 +121,6 @@ export const getDisplayAsset = (asset: string | null) => { return asset ? (asset[0] === 's' ? asset.slice(1) : asset) : null; }; -export const getReasonFromCode = ( - reasonCode?: BigNumber -): MarketClosureReason | 'unknown' | null => { - switch (Number(reasonCode)) { - case 1: - return 'system-upgrade'; - case 2: - return 'market-closure'; - case 3: - case 55: - case 65: - case 231: - return 'circuit-breaker'; - case 99999: - return 'emergency'; - default: - return 'unknown'; - } -}; - export const calculateVolumes = ( futuresHourlyStats: FuturesAggregateStatResult[] ): FuturesVolumes => { @@ -522,7 +501,7 @@ const mapOrderType = (orderType: Partial): FuturesOrderTypeDi }; export const mapTrades = (futuresTrades: FuturesTradeResult[]): FuturesTrade[] => { - return futuresTrades?.map( + return futuresTrades.map( ({ id, timestamp, @@ -533,9 +512,10 @@ export const mapTrades = (futuresTrades: FuturesTradeResult[]): FuturesTrade[] = positionClosed, pnl, feesPaid, + keeperFeesPaid, orderType, accountType, - }: FuturesTradeResult) => { + }) => { return { asset, accountType, @@ -548,6 +528,7 @@ export const mapTrades = (futuresTrades: FuturesTradeResult[]): FuturesTrade[] = side: size.gt(0) ? PositionSide.LONG : PositionSide.SHORT, pnl: new Wei(pnl, 18, true), feesPaid: new Wei(feesPaid, 18, true), + keeperFeesPaid: new Wei(keeperFeesPaid, 18, true), orderType: mapOrderType(orderType), }; } @@ -557,15 +538,8 @@ export const mapTrades = (futuresTrades: FuturesTradeResult[]): FuturesTrade[] = export const mapMarginTransfers = ( marginTransfers: FuturesMarginTransferResult[] ): MarginTransfer[] => { - return marginTransfers?.map( - ({ - timestamp, - account, - market, - size, - asset, - txHash, - }: FuturesMarginTransferResult): MarginTransfer => { + return marginTransfers.map( + ({ timestamp, account, market, size, asset, txHash }): MarginTransfer => { const sizeWei = new Wei(size); const numTimestamp = wei(timestamp).toNumber(); @@ -585,8 +559,8 @@ export const mapMarginTransfers = ( export const mapCrossMarginTransfers = ( marginTransfers: CrossMarginAccountTransferResult[] ): MarginTransfer[] => { - return marginTransfers?.map( - ({ timestamp, account, size, txHash }: CrossMarginAccountTransferResult): MarginTransfer => { + return marginTransfers.map( + ({ timestamp, account, size, txHash }): MarginTransfer => { const sizeWei = new Wei(size); const numTimestamp = wei(timestamp).toNumber(); diff --git a/sdk/utils/synths.ts b/sdk/utils/synths.ts new file mode 100644 index 0000000000..e30376f3be --- /dev/null +++ b/sdk/utils/synths.ts @@ -0,0 +1,20 @@ +import { BigNumber } from 'ethers'; + +export const getReasonFromCode = (reasonCode: BigNumber | number) => { + switch (Number(reasonCode)) { + case 1: + return 'system-upgrade'; + case 2: + return 'market-closure'; + case 3: + case 55: + case 65: + return 'circuit-breaker'; + case 99999: + return 'emergency'; + default: + return 'market-closure'; + } +}; + +export type MarketClosureReason = ReturnType; diff --git a/sections/dashboard/FuturesMarketsTable/FuturesMarketsTable.tsx b/sections/dashboard/FuturesMarketsTable/FuturesMarketsTable.tsx index 52b60d80a4..118b0f1bce 100644 --- a/sections/dashboard/FuturesMarketsTable/FuturesMarketsTable.tsx +++ b/sections/dashboard/FuturesMarketsTable/FuturesMarketsTable.tsx @@ -13,7 +13,6 @@ import { DesktopOnlyView, MobileOrTabletView } from 'components/Media'; import Spacer from 'components/Spacer'; import Table, { TableHeader } from 'components/Table'; import ROUTES from 'constants/routes'; -import Connector from 'containers/Connector'; import { FuturesMarketAsset } from 'sdk/types/futures'; import { getDisplayAsset } from 'sdk/utils/futures'; import { @@ -30,7 +29,6 @@ import { getSynthDescription, MarketKeyByAsset } from 'utils/futures'; const FuturesMarketsTable: FC = () => { const { t } = useTranslation(); const router = useRouter(); - const { synthsMap } = Connector.useContainer(); const futuresMarkets = useAppSelector(selectMarkets); const pastRates = useAppSelector(selectPreviousDayPrices); @@ -41,7 +39,7 @@ const FuturesMarketsTable: FC = () => { let data = useMemo(() => { return futuresMarkets.map((market) => { - const description = getSynthDescription(market.asset, synthsMap, t); + const description = getSynthDescription(market.asset, t); const volume = futuresVolumes[market.marketKey]?.volume; const assetPriceInfo = pricesInfo[market.asset]; const pastPrice = pastRates.find((price) => price.synth === getDisplayAsset(market.asset)); @@ -50,7 +48,6 @@ const FuturesMarketsTable: FC = () => { return { asset: market.asset, market: market.marketName, - synth: synthsMap[market.asset], description, price: marketPrice, priceInfo: assetPriceInfo, @@ -68,7 +65,7 @@ const FuturesMarketsTable: FC = () => { marketClosureReason: market.marketClosureReason, }; }); - }, [synthsMap, futuresMarkets, pastRates, futuresVolumes, markPrices, pricesInfo, t]); + }, [futuresMarkets, pastRates, futuresVolumes, markPrices, pricesInfo, t]); return ( <> diff --git a/sections/dashboard/FuturesPositionsTable/FuturesPositionsTable.tsx b/sections/dashboard/FuturesPositionsTable/FuturesPositionsTable.tsx index fdd3cef635..73708ec592 100644 --- a/sections/dashboard/FuturesPositionsTable/FuturesPositionsTable.tsx +++ b/sections/dashboard/FuturesPositionsTable/FuturesPositionsTable.tsx @@ -16,7 +16,6 @@ import { Body } from 'components/Text'; import { EXTERNAL_LINKS } from 'constants/links'; import { NO_VALUE } from 'constants/placeholder'; import ROUTES from 'constants/routes'; -import Connector from 'containers/Connector'; import useIsL2 from 'hooks/useIsL2'; import useNetworkSwitcher from 'hooks/useNetworkSwitcher'; import { FuturesAccountType } from 'queries/futures/subgraph'; @@ -67,7 +66,6 @@ const FuturesPositionsTable: FC = ({ showEmptyTable = true, }) => { const { t } = useTranslation(); - const { synthsMap } = Connector.useContainer(); const router = useRouter(); const { switchToL2 } = useNetworkSwitcher(); @@ -84,7 +82,7 @@ const FuturesPositionsTable: FC = ({ return positions .map((position) => { const market = futuresMarkets.find((market) => market.asset === position.asset); - const description = getSynthDescription(position.asset, synthsMap, t); + const description = getSynthDescription(position.asset, t); const thisPositionHistory = positionHistory.find((ph) => { return ph.isOpen && ph.asset === position.asset; }); @@ -109,7 +107,6 @@ const FuturesPositionsTable: FC = ({ futuresMarkets, positionHistory, currentMarket, - synthsMap, t, showCurrentMarket, ]); diff --git a/sections/dashboard/Overview/Overview.tsx b/sections/dashboard/Overview/Overview.tsx index a6cf636569..892e01728c 100644 --- a/sections/dashboard/Overview/Overview.tsx +++ b/sections/dashboard/Overview/Overview.tsx @@ -11,6 +11,7 @@ import * as Text from 'components/Text'; import { ETH_ADDRESS, ETH_COINGECKO_ADDRESS } from 'constants/currency'; import Connector from 'containers/Connector'; import { FuturesAccountTypes } from 'queries/futures/types'; +import { SynthSymbol } from 'sdk/data/synths'; import { CompetitionBanner } from 'sections/shared/components/CompetitionBanner'; import { selectBalances } from 'state/balances/selectors'; import { sdk } from 'state/config'; @@ -21,6 +22,7 @@ import { selectFuturesPortfolio, } from 'state/futures/selectors'; import { useAppSelector, useFetchAction } from 'state/hooks'; +import { selectSynthsMap } from 'state/wallet/selectors'; import { formatDollars, toWei, zeroBN } from 'utils/formatters/number'; import logError from 'utils/logError'; @@ -50,7 +52,8 @@ const Overview: FC = () => { ); const [activeMarketsTab] = useState(MarketsTab.FUTURES); - const { network, synthsMap } = Connector.useContainer(); + const { network } = Connector.useContainer(); + const synthsMap = useAppSelector(selectSynthsMap); const { tokenBalances } = useAppSelector(({ balances }) => balances); // Only available on Optimism mainnet @@ -65,7 +68,7 @@ const Overview: FC = () => { try { const exchangeBalances = oneInchEnabled ? Object.values(tokenBalances) - .filter((token) => !synthsMap[token.token.symbol]) + .filter((token) => !synthsMap[token.token.symbol as SynthSymbol]) .map((value) => ({ name: value.token.name, currencyKey: value.token.symbol, diff --git a/sections/dashboard/SpotHistoryTable/SpotHistoryTable.tsx b/sections/dashboard/SpotHistoryTable/SpotHistoryTable.tsx index 1d3db724ab..be5f2ae722 100644 --- a/sections/dashboard/SpotHistoryTable/SpotHistoryTable.tsx +++ b/sections/dashboard/SpotHistoryTable/SpotHistoryTable.tsx @@ -1,4 +1,3 @@ -import { SynthExchangeResult } from '@synthetixio/queries'; import Link from 'next/link'; import { FC, useMemo, ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; @@ -16,24 +15,20 @@ import Connector from 'containers/Connector'; import { blockExplorer } from 'containers/Connector/Connector'; import useSelectedPriceCurrency from 'hooks/useSelectedPriceCurrency'; import useGetWalletTrades from 'queries/synths/useGetWalletTrades'; +import { WalletTradesExchangeResult } from 'sdk/types/synths'; +import { useAppSelector } from 'state/hooks'; +import { selectSynthsMap } from 'state/wallet/selectors'; import { ExternalLink } from 'styles/common'; import TimeDisplay from '../../futures/Trades/TimeDisplay'; -interface SynthTradesExchangeResult extends SynthExchangeResult { - hash: string; -} - -type WalletTradesExchangeResult = Omit & { - timestamp: number; -}; - const conditionalRender = (prop: T, children: ReactElement) => !prop ? {NO_VALUE} : children; const SpotHistoryTable: FC = () => { const { t } = useTranslation(); - const { network, walletAddress, synthsMap } = Connector.useContainer(); + const { network, walletAddress } = Connector.useContainer(); + const synthsMap = useAppSelector(selectSynthsMap); const { selectPriceCurrencyRate, selectedPriceCurrency } = useSelectedPriceCurrency(); const walletTradesQuery = useGetWalletTrades(walletAddress!); @@ -48,7 +43,7 @@ const SpotHistoryTable: FC = () => { () => trades.filter((trade: any) => { const activeSynths = synths.map((synth) => synth.name); - return activeSynths.includes(trade.fromSynth?.symbol as CurrencyKey); + return activeSynths.includes(trade.fromSynth?.symbol); }), [trades, synths] ); @@ -147,11 +142,11 @@ const SpotHistoryTable: FC = () => { Header:
{t('dashboard.history.spot-history-table.usd-value')}
, accessor: 'amount', Cell: (cellProps: CellProps) => { - const currencyKey = cellProps.row.original.toSynth?.symbol as CurrencyKey; + const currencyKey = cellProps.row.original.toSynth?.symbol; return conditionalRender( currencyKey, = memo( const t: FuturesFeeForAccountProps[] = futuresFeeQuery.data ?? []; return t - .map((trade) => formatEther(trade.feesPaid.toString())) + .map((trade) => formatEther(trade.feesPaid.sub(trade.keeperFeesPaid).toString())) .reduce((acc, curr) => acc.add(wei(curr)), zeroBN); }, [futuresFeeQuery.data]); diff --git a/sections/dashboard/SynthBalancesTable/SynthBalancesTable.tsx b/sections/dashboard/SynthBalancesTable/SynthBalancesTable.tsx index 58b2c49580..c94e42eeee 100644 --- a/sections/dashboard/SynthBalancesTable/SynthBalancesTable.tsx +++ b/sections/dashboard/SynthBalancesTable/SynthBalancesTable.tsx @@ -1,5 +1,3 @@ -import { CurrencyKey } from '@synthetixio/contracts-interface'; -import { SynthBalance } from '@synthetixio/queries'; import Wei, { wei } from '@synthetixio/wei'; import { FC, ReactElement, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -12,17 +10,18 @@ import { MobileHiddenView, MobileOnlyView } from 'components/Media'; import Table, { TableNoResults } from 'components/Table'; import { Body } from 'components/Text'; import { NO_VALUE } from 'constants/placeholder'; -import Connector from 'containers/Connector'; +import { SynthSymbol } from 'sdk/data/synths'; import { getDisplayAsset } from 'sdk/utils/futures'; import { selectBalances } from 'state/balances/selectors'; import { useAppSelector } from 'state/hooks'; import { selectPreviousDayPrices, selectPrices } from 'state/prices/selectors'; +import { selectSynthsMap } from 'state/wallet/selectors'; import { sortWei } from 'utils/balances'; import { formatNumber, zeroBN } from 'utils/formatters/number'; import { isDecimalFour } from 'utils/futures'; type Cell = { - synth: CurrencyKey; + synth: SynthSymbol; description: string | undefined; balance: Wei; usdBalance: Wei; @@ -46,17 +45,17 @@ type SynthBalancesTableProps = { const SynthBalancesTable: FC = ({ exchangeTokens }) => { const { t } = useTranslation(); - const { synthsMap } = Connector.useContainer(); const prices = useAppSelector(selectPrices); const pastRates = useAppSelector(selectPreviousDayPrices); const { synthBalances } = useAppSelector(selectBalances); + const synthsMap = useAppSelector(selectSynthsMap); const synthTokens = useMemo(() => { - return synthBalances.map((synthBalance: SynthBalance) => { + return synthBalances.map((synthBalance) => { const { currencyKey, balance, usdBalance } = synthBalance; const price = prices[currencyKey].onChain; const pastPrice = pastRates.find((price) => price.synth === getDisplayAsset(currencyKey)); - const description = synthsMap?.[currencyKey]?.description ?? ''; + const description = synthsMap[currencyKey as SynthSymbol]?.description ?? ''; return { synth: currencyKey, diff --git a/sections/exchange/FooterCard/MarketClosureCard.tsx b/sections/exchange/FooterCard/MarketClosureCard.tsx index 9b5b894127..18c52c971a 100644 --- a/sections/exchange/FooterCard/MarketClosureCard.tsx +++ b/sections/exchange/FooterCard/MarketClosureCard.tsx @@ -2,7 +2,7 @@ import { FC, useMemo, memo } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { DesktopOnlyView, MobileOrTabletView } from 'components/Media'; -import useMarketClosed from 'hooks/useMarketClosed'; +import { SynthSymbol } from 'sdk/data/synths'; import { MessageContainer, Message, @@ -13,22 +13,29 @@ import { useAppSelector } from 'state/hooks'; const MarketClosureCard: FC = memo(() => { const { t } = useTranslation(); - const { quoteCurrencyKey, baseCurrencyKey } = useAppSelector(({ exchange }) => ({ - quoteCurrencyKey: exchange.quoteCurrencyKey, - baseCurrencyKey: exchange.baseCurrencyKey, - })); + const { quoteCurrencyKey, baseCurrencyKey, synthSuspensions } = useAppSelector( + ({ exchange }) => ({ + quoteCurrencyKey: exchange.quoteCurrencyKey, + baseCurrencyKey: exchange.baseCurrencyKey, + synthSuspensions: exchange.synthSuspensions, + }) + ); + + const quoteSuspensionData = quoteCurrencyKey + ? synthSuspensions?.[quoteCurrencyKey as SynthSymbol] + : undefined; - const quoteCurrencyMarketClosed = useMarketClosed(quoteCurrencyKey ?? null); - const baseCurrencyMarketClosed = useMarketClosed(baseCurrencyKey ?? null); + const baseSuspensionData = baseCurrencyKey + ? synthSuspensions?.[baseCurrencyKey as SynthSymbol] + : undefined; const getSuspensionReason = useMemo(() => { - if (baseCurrencyMarketClosed.isMarketClosed && quoteCurrencyMarketClosed.isMarketClosed) { + if (baseSuspensionData?.isSuspended && quoteSuspensionData?.isSuspended) { return 'both-synths-suspended'; } - return ( - baseCurrencyMarketClosed.marketClosureReason || quoteCurrencyMarketClosed.marketClosureReason - ); - }, [baseCurrencyMarketClosed, quoteCurrencyMarketClosed]); + + return baseSuspensionData?.reason || quoteSuspensionData?.reason; + }, [baseSuspensionData, quoteSuspensionData]); return ( <> @@ -41,9 +48,7 @@ const MarketClosureCard: FC = memo(() => { diff --git a/sections/exchange/MobileSwap/SwapInfoBox.tsx b/sections/exchange/MobileSwap/SwapInfoBox.tsx index 374101bd43..dcc6a1c18b 100644 --- a/sections/exchange/MobileSwap/SwapInfoBox.tsx +++ b/sections/exchange/MobileSwap/SwapInfoBox.tsx @@ -1,4 +1,3 @@ -import useSynthetixQueries from '@synthetixio/queries'; import { wei } from '@synthetixio/wei'; import React from 'react'; import { useTranslation } from 'react-i18next'; @@ -8,7 +7,6 @@ import TimerIcon from 'assets/svg/app/timer.svg'; import { InfoBoxContainer, InfoBoxRow } from 'components/InfoBox'; import Tooltip from 'components/Tooltip/Tooltip'; import { NO_VALUE } from 'constants/placeholder'; -import { parseGasPriceObject } from 'hooks/useGas'; import useIsL1 from 'hooks/useIsL1'; import useIsL2 from 'hooks/useIsL2'; import { selectGasPrice, selectGasSpeed } from 'state/app/selectors'; @@ -91,29 +89,15 @@ const GasPriceRow = () => { const customGasPrice = useAppSelector(selectGasPrice); const isL2 = useIsL2(); const isMainnet = useIsL1(); - - const { useEthGasPriceQuery } = useSynthetixQueries(); - const transactionFee = useAppSelector(selectTransactionFeeWei); - const ethGasPriceQuery = useEthGasPriceQuery(); - - const gasPrices = React.useMemo(() => ethGasPriceQuery?.data ?? undefined, [ - ethGasPriceQuery.data, - ]); - - const hasCustomGasPrice = !!customGasPrice; - const gasPrice = gasPrices ? parseGasPriceObject(gasPrices[gasSpeed]) : null; - const formattedTransactionFee = React.useMemo(() => { return transactionFee ? formatDollars(transactionFee, { maxDecimals: 1 }) : NO_VALUE; }, [transactionFee]); const gasPriceItem = isL2 ? formattedTransactionFee - : `${formatNumber(hasCustomGasPrice ? +customGasPrice : gasPrice ?? 0, { - minDecimals: 2, - })} Gwei`; + : `${formatNumber(+customGasPrice, { minDecimals: 2 })} Gwei`; return ( { ? t('common.summary.gas-prices.max-fee') : t('common.summary.gas-prices.gas-price') } - value={gasPrice != null ? gasPriceItem : NO_VALUE} + value={gasPriceItem} /> ); }; diff --git a/sections/exchange/TradeCard/Cards/FooterCard.tsx b/sections/exchange/TradeCard/Cards/FooterCard.tsx index db475d7ea9..c8e166b7a0 100644 --- a/sections/exchange/TradeCard/Cards/FooterCard.tsx +++ b/sections/exchange/TradeCard/Cards/FooterCard.tsx @@ -2,7 +2,7 @@ import { FC, memo } from 'react'; import Connector from 'containers/Connector'; import useIsL2 from 'hooks/useIsL2'; -import useMarketClosed from 'hooks/useMarketClosed'; +import { SynthSymbol } from 'sdk/data/synths'; import ConnectWalletCard from 'sections/exchange/FooterCard/ConnectWalletCard'; import MarketClosureCard from 'sections/exchange/FooterCard/MarketClosureCard'; import TradeSummaryCard from 'sections/exchange/FooterCard/TradeSummaryCard'; @@ -13,6 +13,7 @@ import SettleTransactionsCard from '../../FooterCard/SettleTransactionsCard'; const FooterCard: FC = memo(() => { const { isWalletConnected } = Connector.useContainer(); const isL2 = useIsL2(); + const synthSuspensions = useAppSelector(({ exchange }) => exchange.synthSuspensions); const { quoteCurrencyKey, baseCurrencyKey, numEntries } = useAppSelector(({ exchange }) => ({ quoteCurrencyKey: exchange.quoteCurrencyKey, @@ -20,14 +21,19 @@ const FooterCard: FC = memo(() => { numEntries: exchange.numEntries, })); - const quoteCurrencyMarketClosed = useMarketClosed(quoteCurrencyKey ?? null); - const baseCurrencyMarketClosed = useMarketClosed(baseCurrencyKey ?? null); + const quoteMarketClosed = quoteCurrencyKey + ? synthSuspensions?.[quoteCurrencyKey as SynthSymbol]?.isSuspended + : false; + + const baseMarketClosed = baseCurrencyKey + ? synthSuspensions?.[baseCurrencyKey as SynthSymbol]?.isSuspended + : false; return ( <> {!isWalletConnected ? ( - ) : baseCurrencyMarketClosed.isMarketClosed || quoteCurrencyMarketClosed.isMarketClosed ? ( + ) : baseMarketClosed || quoteMarketClosed ? ( ) : !isL2 && numEntries >= 12 ? ( diff --git a/sections/futures/MobileTrade/MobileTrade.tsx b/sections/futures/MobileTrade/MobileTrade.tsx index caf928749a..dd75ef09ab 100644 --- a/sections/futures/MobileTrade/MobileTrade.tsx +++ b/sections/futures/MobileTrade/MobileTrade.tsx @@ -1,17 +1,38 @@ import React from 'react'; +import styled from 'styled-components'; +import Connector from 'containers/Connector'; +import useIsL2 from 'hooks/useIsL2'; + +import FuturesUnsupportedNetwork from '../Trade/FuturesUnsupported'; import MarketsDropdown from '../Trade/MarketsDropdown'; import OverviewTabs from './OverviewTabs'; import PositionDetails from './PositionDetails'; import UserTabs from './UserTabs'; -const MobileTrade: React.FC = () => ( - <> - - - - - -); +const MobileTrade: React.FC = () => { + const isL2 = useIsL2(); + const { walletAddress } = Connector.useContainer(); + return ( + <> + + + {walletAddress && !isL2 ? ( + + + + ) : ( + <> + + + + )} + + ); +}; + +const SwitchNetworkContainer = styled.div` + padding: 15px; +`; export default MobileTrade; diff --git a/sections/futures/MobileTrade/OverviewTabs/OverviewTabs.tsx b/sections/futures/MobileTrade/OverviewTabs/OverviewTabs.tsx index da9647870a..6a6c3f9261 100644 --- a/sections/futures/MobileTrade/OverviewTabs/OverviewTabs.tsx +++ b/sections/futures/MobileTrade/OverviewTabs/OverviewTabs.tsx @@ -70,6 +70,7 @@ const MainTabButtonsContainer = styled.div` grid-column-gap: 15px; overflow: auto; padding: 0 15px; + margin-top: 5px; `; export default OverviewTabs; diff --git a/sections/futures/MobileTrade/drawers/BaseDrawer.tsx b/sections/futures/MobileTrade/drawers/BaseDrawer.tsx index 99884ebcc5..daf8f2d16f 100644 --- a/sections/futures/MobileTrade/drawers/BaseDrawer.tsx +++ b/sections/futures/MobileTrade/drawers/BaseDrawer.tsx @@ -42,13 +42,9 @@ const BaseDrawer: React.FC = ({ open, closeDrawer, items, butto ); const StyledModal = styled(FullScreenModal)` - top: initial; + top: 0; bottom: 74px; - height: 100%; - - display: flex; - flex-direction: column; - justify-content: flex-end; + overflow: scroll; background-color: transparent; @@ -67,9 +63,6 @@ const StyledModal = styled(FullScreenModal)` `; const Background = styled.div` - display: flex; - flex-direction: column; - justify-content: flex-end; background-color: rgba(0, 0, 0, 0.5); height: 100%; width: 100%; diff --git a/sections/futures/PositionCard/ClosePositionModal.tsx b/sections/futures/PositionCard/ClosePositionModal.tsx index c05b4a6a33..03dc928e8c 100644 --- a/sections/futures/PositionCard/ClosePositionModal.tsx +++ b/sections/futures/PositionCard/ClosePositionModal.tsx @@ -1,5 +1,4 @@ -import Wei, { wei } from '@synthetixio/wei'; -import { useMemo, useEffect, useState } from 'react'; +import { useMemo, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import styled from 'styled-components'; @@ -8,13 +7,16 @@ import Button from 'components/Button'; import Error from 'components/ErrorView'; import { FlexDivCentered, FlexDivCol } from 'components/layout/flex'; import { ButtonLoader } from 'components/Loader/Loader'; -import Connector from 'containers/Connector'; -import { getFuturesMarketContract } from 'queries/futures/utils'; import { FuturesFilledPosition, PositionSide } from 'sdk/types/futures'; -import { selectIsClosingPosition, selectMarketAsset } from 'state/futures/selectors'; -import { useAppSelector } from 'state/hooks'; +import { getClosePositionOrderFee } from 'state/futures/actions'; +import { + selectClosePositionOrderFee, + selectClosePositionOrderFeeError, + selectIsClosingPosition, + selectMarketAsset, +} from 'state/futures/selectors'; +import { useAppDispatch, useAppSelector } from 'state/hooks'; import { formatCurrency, formatDollars, formatNumber, zeroBN } from 'utils/formatters/number'; -import logError from 'utils/logError'; type ClosePositionModalProps = { positionDetails: FuturesFilledPosition | null | undefined; @@ -32,32 +34,19 @@ function ClosePositionModal({ onClosePosition, }: ClosePositionModalProps) { const { t } = useTranslation(); - const { defaultSynthetixjs: synthetixjs, synthsMap } = Connector.useContainer(); + const dispatch = useAppDispatch(); const marketAsset = useAppSelector(selectMarketAsset); const isClosing = useAppSelector(selectIsClosingPosition); - const [orderFee, setOrderFee] = useState(wei(0)); - const [error, setError] = useState(null); + const orderFee = useAppSelector(selectClosePositionOrderFee); + const error = useAppSelector(selectClosePositionOrderFeeError); const positionSize = useMemo(() => positionDetails?.size ?? zeroBN, [positionDetails?.size]); useEffect(() => { - const getOrderFee = async () => { - try { - if (!synthetixjs || !marketAsset || !positionSize) return; - setError(null); - const FuturesMarketContract = getFuturesMarketContract(marketAsset, synthetixjs!.contracts); - const size = positionSize.neg(); - const orderFee = await FuturesMarketContract.orderFee(size.toBN()); - setOrderFee(wei(orderFee.fee)); - } catch (e) { - logError(e); - setError(e?.data?.message ?? e.message); - } - }; - getOrderFee(); - }, [synthetixjs, marketAsset, positionSize]); + dispatch(getClosePositionOrderFee()); + }, [marketAsset, positionSize, dispatch]); const dataRows = useMemo(() => { if (!positionDetails || !marketAsset) return []; @@ -72,9 +61,7 @@ function ClosePositionModal({ }, { label: t('futures.market.user.position.modal.size'), - value: formatCurrency(marketAsset || '', positionDetails?.size ?? zeroBN, { - sign: marketAsset ? synthsMap[marketAsset]?.sign : '', - }), + value: formatCurrency(marketAsset, positionDetails?.size ?? zeroBN), }, { label: t('futures.market.user.position.modal.leverage'), @@ -89,7 +76,7 @@ function ClosePositionModal({ value: formatDollars(orderFee), }, ]; - }, [positionDetails, marketAsset, t, orderFee, synthsMap]); + }, [positionDetails, marketAsset, t, orderFee]); return ( { - const marketKey = useAppSelector(selectMarketKey); const openModal = useAppSelector(selectOpenModal); - const { isFuturesMarketClosed } = useFuturesMarketClosed(marketKey); + const marketInfo = useAppSelector(selectMarketInfo); return ( <> {openModal === 'futures_cross_leverage' && } - + @@ -231,14 +229,11 @@ const RealizedPNLRow = memo(() => { .add(positionHistory?.netFunding) .add(position?.position?.accruedFunding ?? zeroBN) .sub(positionHistory?.feesPaid) ?? zeroBN; - const realizedPnlPct = realizedPnl.abs().gt(0) - ? realizedPnl.div(positionHistory?.initialMargin.add(positionHistory?.totalDeposits)) - : zeroBN; const realizedPnlText = positionHistory && realizedPnl ? `${formatDollars(realizedPnl, { minDecimals: realizedPnl.abs().lt(0.01) ? 4 : 2, - })} (${formatPercent(realizedPnlPct)})` + })}` : NO_VALUE; const rpnlTooltipContent = useMemo(() => { diff --git a/sections/futures/Trade/FuturesUnsupported.tsx b/sections/futures/Trade/FuturesUnsupported.tsx index 68a20cee46..752c423cda 100644 --- a/sections/futures/Trade/FuturesUnsupported.tsx +++ b/sections/futures/Trade/FuturesUnsupported.tsx @@ -1,6 +1,8 @@ import { useTranslation } from 'react-i18next'; import styled from 'styled-components'; +import Button from 'components/Button'; +import { FlexDivCentered } from 'components/layout/flex'; import useNetworkSwitcher from 'hooks/useNetworkSwitcher'; import { BorderedPanel } from 'styles/common'; @@ -11,9 +13,11 @@ const FuturesUnsupportedNetwork = () => { {t('futures.page-title')} {t('common.l2-cta')} - -
{t('homepage.l2.cta-buttons.switch-l2')}
-
+ + +
); }; @@ -22,13 +26,10 @@ const UnsupportedMessage = styled.div` margin-top: 12px; `; -const LinkContainer = styled.div` - margin-top: 12px; - div { - cursor: pointer; - font-size: 12px; - color: ${(props) => props.theme.colors.selectedTheme.button.text.primary}; - } +const ButtonContainer = styled(FlexDivCentered)` + width: 100%; + justify-content: center; + margin-top: 15px; `; const Title = styled.div` diff --git a/sections/futures/Trade/MarketsDropdown.tsx b/sections/futures/Trade/MarketsDropdown.tsx index 53e9b87ff1..d6bc2ea195 100644 --- a/sections/futures/Trade/MarketsDropdown.tsx +++ b/sections/futures/Trade/MarketsDropdown.tsx @@ -14,9 +14,7 @@ import Search from 'components/Table/Search'; import { Body } from 'components/Text'; import NumericValue from 'components/Text/NumericValue'; import ROUTES from 'constants/routes'; -import Connector from 'containers/Connector'; import useClickOutside from 'hooks/useClickOutside'; -import useFuturesMarketClosed from 'hooks/useFuturesMarketClosed'; import { FuturesMarketAsset } from 'sdk/types/futures'; import { getDisplayAsset } from 'sdk/utils/futures'; import { @@ -24,6 +22,7 @@ import { selectMarkets, selectMarketsQueryStatus, selectFuturesType, + selectMarketInfo, selectMarkPriceInfos, } from 'state/futures/selectors'; import { useAppSelector } from 'state/hooks'; @@ -46,17 +45,13 @@ const MarketsDropdown: React.FC = ({ mobile }) => { const futuresMarkets = useAppSelector(selectMarkets); const marketsQueryStatus = useAppSelector(selectMarketsQueryStatus); + const marketInfo = useAppSelector(selectMarketInfo); const [open, setOpen] = useState(false); const [search, setSearch] = useState(''); const { ref } = useClickOutside(() => setOpen(false)); - const { isFuturesMarketClosed, futuresClosureReason } = useFuturesMarketClosed( - MarketKeyByAsset[marketAsset] - ); - const router = useRouter(); - const { synthsMap } = Connector.useContainer(); const { t } = useTranslation(); const getBasePriceRateInfo = useCallback( @@ -87,7 +82,7 @@ const MarketsDropdown: React.FC = ({ mobile }) => { label: getMarketName(market.asset), asset: market.asset, key: market.marketKey, - description: getSynthDescription(market.asset, synthsMap, t), + description: getSynthDescription(market.asset, t), priceNum: basePriceRate?.price.toNumber() ?? 0, price: formatDollars(basePriceRate?.price ?? '0', { suggestDecimals: true }), change: @@ -99,7 +94,7 @@ const MarketsDropdown: React.FC = ({ mobile }) => { closureReason: market.marketClosureReason, }; }); - }, [futuresMarkets, search, synthsMap, t, getBasePriceRateInfo, getPastPrice]); + }, [futuresMarkets, search, t, getBasePriceRateInfo, getPastPrice]); const isFetching = !futuresMarkets.length && marketsQueryStatus.status === FetchStatus.Loading; @@ -110,9 +105,9 @@ const MarketsDropdown: React.FC = ({ mobile }) => { mobile={mobile} asset={marketAsset} label={getMarketName(marketAsset)} - description={getSynthDescription(marketAsset, synthsMap, t)} - isMarketClosed={isFuturesMarketClosed} - closureReason={futuresClosureReason} + description={getSynthDescription(marketAsset, t)} + isMarketClosed={marketInfo?.isSuspended} + closureReason={marketInfo?.marketClosureReason} priceDetails={{ oneDayChange: selectedBasePriceRate?.price && selectedPastPrice?.rate diff --git a/sections/futures/Trade/MarketsDropdownSelector.tsx b/sections/futures/Trade/MarketsDropdownSelector.tsx index 5d3df60c36..ae712e878b 100644 --- a/sections/futures/Trade/MarketsDropdownSelector.tsx +++ b/sections/futures/Trade/MarketsDropdownSelector.tsx @@ -17,7 +17,7 @@ type Props = { asset: FuturesMarketAsset; label: string; description: string; - isMarketClosed: boolean; + isMarketClosed?: boolean; closureReason?: SynthSuspensionReason; mobile?: boolean; priceDetails: { diff --git a/sections/futures/TradingHistory/TradesHistoryTable.tsx b/sections/futures/TradingHistory/TradesHistoryTable.tsx index 8c983cd517..00d18f1fba 100644 --- a/sections/futures/TradingHistory/TradesHistoryTable.tsx +++ b/sections/futures/TradingHistory/TradesHistoryTable.tsx @@ -8,7 +8,6 @@ import { Body } from 'components/Text'; import { NO_VALUE } from 'constants/placeholder'; import { blockExplorer } from 'containers/Connector/Connector'; import useGetFuturesTrades from 'queries/futures/useGetFuturesTrades'; -import { FuturesTrade } from 'sdk/types/futures'; import { selectMarketKey } from 'state/futures/selectors'; import { useAppSelector } from 'state/hooks'; import { formatNumber } from 'utils/formatters/number'; @@ -39,7 +38,7 @@ const TradesHistoryTable: FC = ({ mobile }) => { ? futuresTradesPages .flat() .filter((value) => !!value) - .map((trade: FuturesTrade | null) => { + .map((trade) => { return { value: Number(trade?.price), amount: Number(trade?.size), diff --git a/sections/homepage/Assets.tsx b/sections/homepage/Assets.tsx index 521d9eb4a6..0b71aed490 100644 --- a/sections/homepage/Assets.tsx +++ b/sections/homepage/Assets.tsx @@ -135,7 +135,7 @@ export const PriceChart = ({ asset }: PriceChartProps) => { const Assets = () => { const { t } = useTranslation(); - const { l2SynthsMap, l2Provider } = Connector.useContainer(); + const { l2Provider } = Connector.useContainer(); const activeMarketsTab = MarketsTab.FUTURES; const prices = useAppSelector(selectPrices); @@ -147,7 +147,7 @@ const Assets = () => { const PERPS = useMemo(() => { return futuresMarkets.map((market) => { const marketPrice = prices[market.asset]?.offChain ?? prices[market.asset]?.onChain ?? wei(0); - const description = getSynthDescription(market.asset, l2SynthsMap, t); + const description = getSynthDescription(market.asset, t); const volume = futuresVolumes[market.marketKey]?.volume?.toNumber() ?? 0; const pastPrice = pastRates.find( (price) => price.synth === market.asset || price.synth === market.asset.slice(1) @@ -168,8 +168,7 @@ const Assets = () => { ), }; }); - // eslint-disable-next-line - }, [futuresMarkets, l2SynthsMap, pastRates, futuresVolumes, t]); + }, [futuresMarkets, pastRates, futuresVolumes, t, prices]); const title = ( <> diff --git a/sections/leaderboard/AllTime.tsx b/sections/leaderboard/AllTime.tsx index e5bebb2804..24a019a432 100644 --- a/sections/leaderboard/AllTime.tsx +++ b/sections/leaderboard/AllTime.tsx @@ -12,6 +12,7 @@ import useENSAvatar from 'hooks/useENSAvatar'; import { AccountStat } from 'queries/futures/types'; import { StyledTrader } from 'sections/leaderboard/trader'; import { getMedal } from 'utils/competition'; +import { staticMainnetProvider } from 'utils/network'; type AllTimeProps = { stats: AccountStat[]; @@ -31,7 +32,7 @@ const AllTime: FC = ({ activeTab, }) => { const { t } = useTranslation(); - const { staticMainnetProvider, walletAddress } = Connector.useContainer(); + const { walletAddress } = Connector.useContainer(); if (compact) { const ownPosition = stats.findIndex((i) => { diff --git a/sections/shared/Layout/AppLayout/Header/ConnectionDot.tsx b/sections/shared/Layout/AppLayout/Header/ConnectionDot.tsx index 25fcf46707..04cfc2b234 100644 --- a/sections/shared/Layout/AppLayout/Header/ConnectionDot.tsx +++ b/sections/shared/Layout/AppLayout/Header/ConnectionDot.tsx @@ -1,9 +1,9 @@ -import { NetworkId, NetworkIdByName } from '@synthetixio/contracts-interface'; import React from 'react'; import styled, { useTheme } from 'styled-components'; import Connector from 'containers/Connector'; import useIsL2 from 'hooks/useIsL2'; +import { NetworkId, NetworkIdByName } from 'sdk/types/common'; type ConnectionDotProps = { className?: string; diff --git a/sections/shared/Layout/AppLayout/Header/MobileUserMenu/MobileWalletButton.tsx b/sections/shared/Layout/AppLayout/Header/MobileUserMenu/MobileWalletButton.tsx index 3d297a592d..7058e106b1 100644 --- a/sections/shared/Layout/AppLayout/Header/MobileUserMenu/MobileWalletButton.tsx +++ b/sections/shared/Layout/AppLayout/Header/MobileUserMenu/MobileWalletButton.tsx @@ -1,11 +1,11 @@ import { useChainModal, useConnectModal } from '@rainbow-me/rainbowkit'; -import { NetworkId } from '@synthetixio/contracts-interface'; import React from 'react'; import { useTranslation } from 'react-i18next'; import styled from 'styled-components'; import Button from 'components/Button'; import Connector from 'containers/Connector'; +import { NetworkId } from 'sdk/types/common'; import { isSupportedNetworkId } from 'utils/network'; import ConnectionDot from '../ConnectionDot'; diff --git a/sections/shared/SystemStatus.tsx b/sections/shared/SystemStatus.tsx index 17ad440504..4789642281 100644 --- a/sections/shared/SystemStatus.tsx +++ b/sections/shared/SystemStatus.tsx @@ -1,4 +1,3 @@ -import useSynthetixQueries from '@synthetixio/queries'; import Head from 'next/head'; import { FC, memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -13,6 +12,7 @@ import { GridDivCenteredCol } from 'components/layout/grid'; import { EXTERNAL_LINKS, PROD_HOSTNAME } from 'constants/links'; import { HEADER_HEIGHT } from 'constants/ui'; import Logo from 'sections/shared/Layout/Logo'; +import { useAppSelector } from 'state/hooks'; import { PageContent, FullScreenContainer, ExternalLink } from 'styles/common'; import media from 'styles/media'; @@ -43,17 +43,12 @@ export const REFRESH_INTERVAL = 2 * 60 * 1000; // 2 min const SystemStatus: FC = memo(({ children }) => { const { t } = useTranslation(); - const { useIsSystemOnMaintenance } = useSynthetixQueries(); - - // current onchain state ( no interval for now, should be added when we are close to a release to save requests ) - const isSystemOnMaintenanceQuery = useIsSystemOnMaintenance({ - refetchInterval: REFRESH_INTERVAL, - }); + const synthetixOnMaintenance = useAppSelector(({ app }) => app.synthetixOnMaintenance); const appOnMaintenance = typeof window !== 'undefined' && window.location.hostname === PROD_HOSTNAME && - (isSystemOnMaintenanceQuery.isSuccess ? isSystemOnMaintenanceQuery.data : false); + synthetixOnMaintenance; return appOnMaintenance ? ( <> diff --git a/sections/shared/components/GasPriceSelect.tsx b/sections/shared/components/GasPriceSelect.tsx index f63620b42e..d337de9717 100644 --- a/sections/shared/components/GasPriceSelect.tsx +++ b/sections/shared/components/GasPriceSelect.tsx @@ -1,13 +1,11 @@ -import useSynthetixQueries from '@synthetixio/queries'; import { FC, useMemo, memo } from 'react'; import { useTranslation } from 'react-i18next'; import { NO_VALUE } from 'constants/placeholder'; -import { parseGasPriceObject } from 'hooks/useGas'; import useIsL1 from 'hooks/useIsL1'; import useIsL2 from 'hooks/useIsL2'; import { SummaryItem, SummaryItemValue, SummaryItemLabel } from 'sections/exchange/summary'; -import { selectGasPrice, selectGasSpeed } from 'state/app/selectors'; +import { selectGasPrice } from 'state/app/selectors'; import { selectTransactionFeeWei } from 'state/exchange/selectors'; import { useAppSelector } from 'state/hooks'; import { formatNumber, formatDollars } from 'utils/formatters/number'; @@ -17,30 +15,17 @@ type GasPriceSelectProps = { }; const GasPriceItem: FC = memo(() => { - const gasSpeed = useAppSelector(selectGasSpeed); const customGasPrice = useAppSelector(selectGasPrice); const isL2 = useIsL2(); - const { useEthGasPriceQuery } = useSynthetixQueries(); - const ethGasPriceQuery = useEthGasPriceQuery(); - const gasPrices = useMemo(() => ethGasPriceQuery?.data, [ethGasPriceQuery.data]); const transactionFee = useAppSelector(selectTransactionFeeWei); const formattedTransactionFee = useMemo(() => { return transactionFee ? formatDollars(transactionFee, { maxDecimals: 1 }) : NO_VALUE; }, [transactionFee]); - const hasCustomGasPrice = !!customGasPrice; - const gasPrice = gasPrices ? parseGasPriceObject(gasPrices[gasSpeed]) : null; - - if (!gasPrice) return <>{NO_VALUE}; - return ( - {isL2 - ? formattedTransactionFee - : `${formatNumber(hasCustomGasPrice ? +customGasPrice : gasPrice ?? 0, { - minDecimals: 2, - })} Gwei`} + {isL2 ? formattedTransactionFee : `${formatNumber(+customGasPrice, { minDecimals: 2 })} Gwei`} ); }); @@ -52,9 +37,7 @@ const GasPriceSelect: FC = memo(({ ...rest }) => { return ( - {isMainnet - ? t('common.summary.gas-prices.max-fee') - : t('common.summary.gas-prices.gas-price')} + {t(`common.summary.gas-prices.${isMainnet ? 'max-fee' : 'gas-price'}`)} diff --git a/sections/shared/modals/CurrencyRow.tsx b/sections/shared/modals/CurrencyRow.tsx index 6c683415d2..ceb57e6625 100644 --- a/sections/shared/modals/CurrencyRow.tsx +++ b/sections/shared/modals/CurrencyRow.tsx @@ -1,14 +1,14 @@ -import { CurrencyKey } from '@synthetixio/contracts-interface'; import Wei from '@synthetixio/wei'; -import { FC } from 'react'; +import { FC, memo } from 'react'; import { useTranslation } from 'react-i18next'; import styled from 'styled-components'; import Currency from 'components/Currency'; import { NO_VALUE } from 'constants/placeholder'; import Connector from 'containers/Connector'; -import useMarketClosed from 'hooks/useMarketClosed'; import useSelectedPriceCurrency from 'hooks/useSelectedPriceCurrency'; +import { SynthSymbol } from 'sdk/data/synths'; +import { useAppSelector } from 'state/hooks'; import { SelectableCurrencyRow } from 'styles/common'; type Token = { @@ -29,36 +29,29 @@ type SynthRowProps = { balance?: TokenBalance; onClick: () => void; }; -const CurrencyRow: FC = ({ token, onClick, balance }) => { + +const CurrencyRow: FC = memo(({ token, onClick, balance }) => { const { t } = useTranslation(); const { isWalletConnected } = Connector.useContainer(); const { selectPriceCurrencyRate, selectedPriceCurrency } = useSelectedPriceCurrency(); - + const synthSuspensions = useAppSelector(({ exchange }) => exchange.synthSuspensions); const currencyKey = token.symbol; - const { marketClosureReason } = useMarketClosed( - token.isSynth ? (currencyKey as CurrencyKey) : null - ); + const reason = token.isSynth + ? synthSuspensions?.[token.symbol as SynthSymbol]?.reason + : undefined; return ( - + {isWalletConnected ? ( = ({ token, onClick, balance }) => { )} ); -}; +}); const StyledSelectableCurrencyRow = styled(SelectableCurrencyRow)` padding: 5px 16px; diff --git a/sections/shared/modals/SelectCurrencyModal.tsx b/sections/shared/modals/SelectCurrencyModal.tsx index 5232ae16f9..d311a4dbe5 100644 --- a/sections/shared/modals/SelectCurrencyModal.tsx +++ b/sections/shared/modals/SelectCurrencyModal.tsx @@ -1,4 +1,3 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; import { wei } from '@synthetixio/wei'; import orderBy from 'lodash/orderBy'; import { FC, useMemo, useState } from 'react'; @@ -15,6 +14,7 @@ import { DEFAULT_SEARCH_DEBOUNCE_MS } from 'constants/defaults'; import useDebouncedMemo from 'hooks/useDebouncedMemo'; import useCoinGeckoTokenPricesQuery from 'queries/coingecko/useCoinGeckoTokenPricesQuery'; import { getSynthsListForNetwork, SynthSymbol } from 'sdk/data/synths'; +import { NetworkId } from 'sdk/types/common'; import { selectBalances, selectBalancesFetchStatus, @@ -85,7 +85,7 @@ export const SelectCurrencyModal: FC = ({ synthsList, [ (synth) => { - const synthBalance = synthBalancesMap[synth.name as CurrencyKey]; + const synthBalance = synthBalancesMap[synth.name]; return !!synthBalance ? Number(synthBalance.usdBalance) : 0; }, (synth) => synth.name.toLowerCase(), @@ -98,8 +98,8 @@ export const SelectCurrencyModal: FC = ({ const combinedTokenList = useMemo(() => { const withSynthTokensCombined = [ - ...tokenList?.filter((i) => !synthKeys.includes(i.symbol as SynthSymbol)), - ...synthsResults?.map((synthToken) => ({ symbol: synthToken?.name, ...synthToken })), + ...tokenList.filter((i) => !synthKeys.includes(i.symbol as SynthSymbol)), + ...synthsResults.map((synthToken) => ({ symbol: synthToken?.name, ...synthToken })), ]; return withSynthTokensCombined; }, [synthKeys, synthsResults, tokenList]); @@ -108,7 +108,7 @@ export const SelectCurrencyModal: FC = ({ () => assetSearch ? combinedTokenList - .filter(({ name, symbol }: any) => { + .filter(({ name, symbol }) => { const assetSearchLC = assetSearch.toLowerCase(); return ( name.toLowerCase().includes(assetSearchLC) || @@ -142,10 +142,7 @@ export const SelectCurrencyModal: FC = ({ } return token; }), - [ - ({ usdBalance }) => (usdBalance ? usdBalance.toNumber() : 0), - ({ symbol }) => symbol.toLowerCase(), - ], + [({ usdBalance }) => usdBalance?.toNumber() ?? 0, ({ symbol }) => symbol.toLowerCase()], ['desc', 'asc'] ) : searchFilteredTokens; @@ -200,12 +197,8 @@ export const SelectCurrencyModal: FC = ({ onSelect(currencyKey, false); onDismiss(); }} - balance={synthBalancesMap[currencyKey as CurrencyKey]} - token={{ - name: synth.description, - symbol: synth.name, - isSynth: true, - }} + balance={synthBalancesMap[currencyKey]} + token={{ name: synth.description, symbol: synth.name, isSynth: true }} /> ); }) @@ -223,15 +216,7 @@ export const SelectCurrencyModal: FC = ({ onSelect(currencyKey, true); onDismiss(); }} - balance={ - balance && usdBalance - ? { - currencyKey, - balance, - usdBalance, - } - : undefined - } + balance={balance && usdBalance ? { currencyKey, balance, usdBalance } : undefined} token={{ ...token, isSynth: false }} /> ); @@ -268,7 +253,6 @@ const SearchContainer = styled.div` const AssetSearchInput = styled(Input).attrs({ type: 'search' })` font-size: 16px; height: 40px; - font-family: ${(props) => props.theme.fonts.regular}; ::placeholder { text-transform: capitalize; color: ${(props) => props.theme.colors.selectedTheme.button.secondary}; diff --git a/sections/shared/modals/TxConfirmationModal.tsx b/sections/shared/modals/TxConfirmationModal.tsx index e7aee39e14..7dd40544e9 100644 --- a/sections/shared/modals/TxConfirmationModal.tsx +++ b/sections/shared/modals/TxConfirmationModal.tsx @@ -1,7 +1,4 @@ -import { CurrencyKey } from '@synthetixio/contracts-interface'; -import useSynthetixQueries from '@synthetixio/queries'; -import { wei } from '@synthetixio/wei'; -import { FC, useMemo } from 'react'; +import { FC, useCallback } from 'react'; import { useTranslation, Trans } from 'react-i18next'; import styled from 'styled-components'; @@ -14,19 +11,12 @@ import Error from 'components/ErrorView'; import { FlexDivRowCentered, FlexDivColCentered } from 'components/layout/flex'; import Tooltip from 'components/Tooltip/Tooltip'; import { ESTIMATE_VALUE } from 'constants/placeholder'; -import Connector from 'containers/Connector'; -import useCurrencyPrice from 'hooks/useCurrencyPrice'; -import useIsL2 from 'hooks/useIsL2'; import { MessageButton } from 'sections/exchange/message'; import { closeModal } from 'state/exchange/reducer'; import { selectEstimatedBaseTradePrice } from 'state/exchange/selectors'; import { useAppDispatch, useAppSelector } from 'state/hooks'; import { numericValueCSS, NoTextTransform } from 'styles/common'; -import { - formatCurrency, - formatDollars, - LONG_CRYPTO_CURRENCY_DECIMALS, -} from 'utils/formatters/number'; +import { formatCurrency, LONG_CRYPTO_CURRENCY_DECIMALS } from 'utils/formatters/number'; export type TxProvider = 'synthetix' | '1inch' | 'synthswap'; @@ -36,12 +26,8 @@ type TxConfirmationModalProps = { export const TxConfirmationModal: FC = ({ attemptRetry }) => { const { t } = useTranslation(); - const { walletAddress } = Connector.useContainer(); - const isL2 = useIsL2(); const dispatch = useAppDispatch(); - const { subgraph } = useSynthetixQueries(); - const { baseCurrencyKey, quoteCurrencyKey, @@ -66,32 +52,10 @@ export const TxConfirmationModal: FC = ({ attemptRetry formatCurrency(baseCurrencyKey!, baseAmount, { minDecimals: decimals, }); - const priceUSD = useCurrencyPrice((quoteCurrencyKey ?? '') as CurrencyKey); - const onDismiss = () => { + const onDismiss = useCallback(() => { dispatch(closeModal()); - }; - - const priceAdjustmentQuery = subgraph.useGetExchangeEntrySettleds( - { where: { from: walletAddress } }, - { reclaim: true, rebate: true } - ); - - const priceAdjustment = useMemo( - () => - priceAdjustmentQuery.data?.length - ? { - rebate: priceAdjustmentQuery.data[0].rebate, - reclaim: priceAdjustmentQuery.data[0].reclaim, - } - : { rebate: wei(0), reclaim: wei(0) }, - [priceAdjustmentQuery.data] - ); - - const priceAdjustmentFeeUSD = useMemo( - () => priceAdjustment.rebate.sub(priceAdjustment.reclaim).mul(priceUSD), - [priceAdjustment, priceUSD] - ); + }, [dispatch]); return ( @@ -197,7 +161,7 @@ export const TxConfirmationModal: FC = ({ attemptRetry - {!isL2 && priceAdjustmentFeeUSD ? ( + {/* {!isL2 && priceAdjustmentFeeUSD ? ( = ({ attemptRetry {formatDollars(priceAdjustmentFeeUSD)} - ) : null} + ) : null} */} {txProvider === '1inch' && ( @@ -326,12 +290,6 @@ const ExchangeFeeHintTooltip = styled(Tooltip)` margin: 0px 0px 0px 40px; `; -const PriceAdjustmentTooltip = styled(Tooltip)` - width: 240px; - padding: 10px; - margin: 0px; -`; - export const TooltipItem = styled.span` display: inline-flex; align-items: center; diff --git a/state/app/actions.ts b/state/app/actions.ts new file mode 100644 index 0000000000..81363f6d06 --- /dev/null +++ b/state/app/actions.ts @@ -0,0 +1,10 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; + +import { ThunkConfig } from 'state/types'; + +export const checkSynthetixStatus = createAsyncThunk( + 'app/checkSynthetixStatus', + (_, { extra: { sdk } }) => { + return sdk.system.getSynthetixStatus(); + } +); diff --git a/state/app/hooks.ts b/state/app/hooks.ts index 5823a82e63..79cda604b9 100644 --- a/state/app/hooks.ts +++ b/state/app/hooks.ts @@ -2,29 +2,34 @@ import { useEffect } from 'react'; import { fetchBalances } from 'state/balances/actions'; import { sdk } from 'state/config'; -import { fetchMarkets } from 'state/futures/actions'; import { selectMarkets } from 'state/futures/selectors'; import { useAppDispatch, useAppSelector, usePollAction } from 'state/hooks'; import { fetchPreviousDayPrices, updatePrices } from 'state/prices/actions'; import { setConnectionError } from 'state/prices/reducer'; -import { selectWallet } from 'state/wallet/selectors'; +import { selectNetwork, selectWallet } from 'state/wallet/selectors'; import { serializePrices } from 'utils/futures'; +import { checkSynthetixStatus } from './actions'; + export function useAppData(ready: boolean) { const dispatch = useAppDispatch(); const wallet = useAppSelector(selectWallet); const markets = useAppSelector(selectMarkets); + const network = useAppSelector(selectNetwork); - usePollAction('fetchMarkets', fetchMarkets, { dependencies: [wallet] }); - - usePollAction('fetchBalances', fetchBalances, { dependencies: [wallet] }); + usePollAction('fetchBalances', fetchBalances, { dependencies: [wallet, network] }); usePollAction('fetchPreviousDayPrices', fetchPreviousDayPrices, { intervalTime: 60000 * 15, - dependencies: [markets.length], + dependencies: [markets.length, network], disabled: !markets.length, }); + usePollAction('checkSynthetixStatus', checkSynthetixStatus, { + intervalTime: 2 * 60 * 1000, + dependencies: [network], + }); + useEffect(() => { if (ready) { sdk.prices.startPriceUpdates(15000); diff --git a/state/app/reducer.ts b/state/app/reducer.ts index f863a0895e..268784653e 100644 --- a/state/app/reducer.ts +++ b/state/app/reducer.ts @@ -4,6 +4,7 @@ import { notifyError } from 'components/ErrorView/ErrorNotifier'; import { TransactionStatus } from 'sdk/types/common'; import { isUserDeniedError } from 'utils/formatters/error'; +import { checkSynthetixStatus } from './actions'; import { AppState, GasPrice, ModalType, Transaction } from './types'; export const APP_INITIAL_STATE: AppState = { @@ -15,6 +16,7 @@ export const APP_INITIAL_STATE: AppState = { gasPrice: '0', }, gasSpeed: 'fast', + synthetixOnMaintenance: false, }; const appSlice = createSlice({ @@ -52,6 +54,11 @@ const appSlice = createSlice({ } }, }, + extraReducers: (builder) => { + builder.addCase(checkSynthetixStatus.fulfilled, (state, action) => { + state.synthetixOnMaintenance = action.payload; + }); + }, }); export const { diff --git a/state/app/types.ts b/state/app/types.ts index b38b61b4a8..e17dae513e 100644 --- a/state/app/types.ts +++ b/state/app/types.ts @@ -37,4 +37,5 @@ export type AppState = { gasSpeed: GasSpeed; gasPrice: GasPrice; transaction?: Transaction | undefined; + synthetixOnMaintenance: boolean; }; diff --git a/state/exchange/actions.ts b/state/exchange/actions.ts index 1cb47443c9..a7c6bf4275 100644 --- a/state/exchange/actions.ts +++ b/state/exchange/actions.ts @@ -117,9 +117,12 @@ export const fetchTokenList = createAsyncThunk( 'exchange/fetchTokenList', async (_, { extra: { sdk } }) => { const synthsMap = sdk.exchange.getSynthsMap(); - const { tokensMap, tokenList } = await sdk.exchange.getOneInchTokens(); + const [{ tokensMap, tokenList }, synthSuspensions] = await Promise.all([ + sdk.exchange.getOneInchTokens(), + sdk.exchange.getSynthSuspensions(), + ]); - return { synthsMap, tokensMap, tokenList }; + return { synthsMap, tokensMap, tokenList, synthSuspensions }; } ); diff --git a/state/exchange/reducer.ts b/state/exchange/reducer.ts index d59000ab74..4efc7869f2 100644 --- a/state/exchange/reducer.ts +++ b/state/exchange/reducer.ts @@ -49,6 +49,7 @@ export const EXCHANGES_INITIAL_STATE: ExchangeState = { txError: undefined, isApproved: undefined, allowance: undefined, + synthSuspensions: {} as ExchangeState['synthSuspensions'], }; const exchangeSlice = createSlice({ @@ -167,6 +168,7 @@ const exchangeSlice = createSlice({ state.synthsMap = action.payload.synthsMap; state.tokensMap = action.payload.tokensMap; state.tokenList = action.payload.tokenList; + state.synthSuspensions = action.payload.synthSuspensions; }); builder.addCase(fetchTokenList.rejected, (state) => { state.tokenListStatus = FetchStatus.Error; diff --git a/state/exchange/types.ts b/state/exchange/types.ts index cbf43faed5..9687f8ab28 100644 --- a/state/exchange/types.ts +++ b/state/exchange/types.ts @@ -1,3 +1,5 @@ +import { SynthSymbol } from 'sdk/data/synths'; +import { SynthSuspensionReason } from 'sdk/types/futures'; import { Token } from 'sdk/types/tokens'; import { FetchStatus } from 'state/types'; @@ -37,4 +39,12 @@ export type ExchangeState = { txError?: string; isApproved?: boolean; allowance?: string; + synthSuspensions: Record< + SynthSymbol, + { + isSuspended: boolean; + reason: SynthSuspensionReason; + reasonCode: number; + } + >; }; diff --git a/state/futures/actions.ts b/state/futures/actions.ts index 5417c8e566..eeec9593be 100644 --- a/state/futures/actions.ts +++ b/state/futures/actions.ts @@ -1,5 +1,4 @@ import { createAsyncThunk } from '@reduxjs/toolkit'; -import { NetworkId } from '@synthetixio/contracts-interface'; import Wei, { wei } from '@synthetixio/wei'; import { BigNumber, ethers } from 'ethers'; import { debounce } from 'lodash'; @@ -7,11 +6,12 @@ import KwentaSDK from 'sdk'; import { notifyError } from 'components/ErrorView/ErrorNotifier'; import { ORDER_KEEPER_ETH_DEPOSIT } from 'constants/futures'; -import { FuturesAccountType } from 'queries/futures/types'; +import { NetworkId } from 'sdk/types/common'; import { TransactionStatus } from 'sdk/types/common'; import { CrossMarginOrderType, DelayedOrder, + FuturesAccountType, FuturesMarket, FuturesOrder, FuturesPosition, @@ -359,15 +359,6 @@ export const fetchMarginTransfers = createAsyncThunk< } }); -export const fetchDashboardFuturesData = createAsyncThunk( - 'futures/fetchDashboardFuturesData', - async (_, { dispatch }) => { - await dispatch(fetchMarkets()); - dispatch(fetchCrossMarginBalanceInfo()); - dispatch(fetchCrossMarginOpenOrders()); - } -); - export const fetchCrossMarginAccountData = createAsyncThunk( 'futures/fetchCrossMarginAccountData', async (_, { dispatch }) => { @@ -497,7 +488,7 @@ export const fetchIsolatedMarginTradePreview = createAsyncThunk< export const fetchCrossMarginTradePreview = createAsyncThunk< FuturesPotentialTradeDetails | null, - { price?: Wei | undefined; sizeDelta: Wei; marginDelta: Wei }, + { price?: Wei; sizeDelta: Wei; marginDelta: Wei }, ThunkConfig >( 'futures/fetchCrossMarginTradePreview', @@ -535,11 +526,12 @@ export const fetchCrossMarginTradePreview = createAsyncThunk< export const clearTradeInputs = createAsyncThunk( 'futures/clearTradeInputs', async (_, { dispatch }) => { - dispatch(editCrossMarginSize('0', 'usd')); - dispatch(editIsolatedMarginSize('0', 'usd')); dispatch(setCrossMarginMarginDelta('0')); dispatch(setCrossMarginFees(ZERO_CM_FEES)); dispatch(setIsolatedMarginFee('0')); + dispatch(setIsolatedMarginLeverageInput('')); + dispatch(setIsolatedTradePreview(null)); + dispatch(setCrossMarginTradePreview(null)); } ); @@ -634,7 +626,6 @@ export const editIsolatedMarginSize = (size: string, currencyType: 'usd' | 'nati const position = selectPosition(getState()); if ( size === '' || - Number(size) === 0 || assetRate.eq(0) || !position?.remainingMargin || position?.remainingMargin.eq(0) @@ -647,9 +638,11 @@ export const editIsolatedMarginSize = (size: string, currencyType: 'usd' | 'nati const nativeSize = currencyType === 'native' ? size : wei(size).div(assetRate).toString(); const usdSize = currencyType === 'native' ? stipZeros(assetRate.mul(size).toString()) : size; - const leverage = wei(usdSize).div(position?.remainingMargin); - dispatch(setIsolatedMarginLeverageInput(leverage.toNumber().toFixed(2))); - + const leverage = + Number(usdSize) > 0 && position?.remainingMargin.gt(0) + ? wei(usdSize).div(position?.remainingMargin).toString(2) + : ''; + dispatch(setIsolatedMarginLeverageInput(leverage)); dispatch( setIsolatedMarginTradeInputs({ susdSize: usdSize, @@ -1321,3 +1314,24 @@ const monitorAndAwaitTransaction = async ( await tx.wait(); dispatch(updateTransactionStatus(TransactionStatus.Confirmed)); }; + +export const getClosePositionOrderFee = createAsyncThunk( + 'futures/getClosePositionOrderFee', + async (_, { getState, extra: { sdk } }) => { + const state = getState(); + const position = selectPosition(state); + const marketInfo = selectMarketInfo(state); + try { + if (!marketInfo) { + throw new Error('No market found'); + } else if (!position?.position) { + throw new Error('No active position in selected market'); + } else { + return sdk.futures.getOrderFee(marketInfo.market, position.position.size.neg()); + } + } catch (err) { + notifyError('Failed to get order fee', err); + throw err; + } + } +); diff --git a/state/futures/reducer.ts b/state/futures/reducer.ts index 4d6bdf3317..06fdfefa74 100644 --- a/state/futures/reducer.ts +++ b/state/futures/reducer.ts @@ -1,8 +1,8 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { NetworkId } from '@synthetixio/contracts-interface'; import { DEFAULT_FUTURES_MARGIN_TYPE, DEFAULT_PRICE_IMPACT_DELTA } from 'constants/defaults'; import { ORDER_PREVIEW_ERRORS } from 'queries/futures/constants'; +import { NetworkId } from 'sdk/types/common'; import { FuturesMarket, FuturesMarketAsset, @@ -43,6 +43,7 @@ import { fetchAllTradesForAccount, fetchIsolatedOpenOrders, fetchMarginTransfers, + getClosePositionOrderFee, } from './actions'; import { CrossMarginState, @@ -84,6 +85,7 @@ export const FUTURES_INITIAL_STATE: FuturesState = { selectedTraderPositionHistory: DEFAULT_QUERY_STATUS, trades: DEFAULT_QUERY_STATUS, marginTransfers: DEFAULT_QUERY_STATUS, + closePositionOrderFee: DEFAULT_QUERY_STATUS, }, transactionEstimations: {} as TransactionEstimations, crossMargin: { @@ -124,6 +126,7 @@ export const FUTURES_INITIAL_STATE: FuturesState = { tradeFee: '0', leverageInput: '0', }, + closePositionOrderFee: '0', }; const futuresSlice = createSlice({ @@ -625,6 +628,17 @@ const futuresSlice = createSlice({ status: FetchStatus.Error, }; }); + builder.addCase(getClosePositionOrderFee.fulfilled, (futuresState, { payload }) => { + futuresState.queryStatuses.closePositionOrderFee = SUCCESS_STATUS; + futuresState.closePositionOrderFee = payload.toString(); + }); + builder.addCase(getClosePositionOrderFee.rejected, (futuresState, { error }) => { + futuresState.queryStatuses.closePositionOrderFee = { + error: error.message, + status: FetchStatus.Error, + }; + futuresState.closePositionOrderFee = '0'; + }); }, }); diff --git a/state/futures/selectors.ts b/state/futures/selectors.ts index 81194a3a51..9711b29b19 100644 --- a/state/futures/selectors.ts +++ b/state/futures/selectors.ts @@ -667,7 +667,7 @@ export const selectMarginTransfers = createSelector( (state: RootState) => state.futures, (wallet, network, type, asset, futures) => { if (!wallet) return []; - const account = futures[accountType(type)].accounts[network][wallet]; + const account = futures[accountType(type)].accounts[network]?.[wallet]; const marginTransfers = account?.marginTransfers ?? []; return marginTransfers.filter( (o) => accountType(type) === 'isolatedMargin' && o.asset === asset @@ -1031,3 +1031,11 @@ export const selectMarketSuspended = createSelector( selectMarketInfo, (marketInfo) => marketInfo?.isSuspended ); + +export const selectClosePositionOrderFee = createSelector( + (state: RootState) => state.futures.closePositionOrderFee, + wei +); + +export const selectClosePositionOrderFeeError = (state: RootState) => + state.futures.queryStatuses.closePositionOrderFee.error; diff --git a/state/futures/types.ts b/state/futures/types.ts index d9b19a8dc0..1394480878 100644 --- a/state/futures/types.ts +++ b/state/futures/types.ts @@ -1,6 +1,5 @@ import Wei from '@synthetixio/wei'; -import { FuturesAccountType } from 'queries/futures/types'; import { TransactionStatus } from 'sdk/types/common'; import { CrossMarginOrderType, @@ -17,6 +16,7 @@ import { FuturesMarketKey, FuturesMarketAsset, MarginTransfer, + FuturesAccountType, } from 'sdk/types/futures'; import { PricesInfo } from 'state/prices/types'; import { QueryStatus } from 'state/types'; @@ -64,6 +64,7 @@ export type FuturesQueryStatuses = { trades: QueryStatus; selectedTraderPositionHistory: QueryStatus; marginTransfers: QueryStatus; + closePositionOrderFee: QueryStatus; }; export type FuturesTransactionType = @@ -174,6 +175,7 @@ export type FuturesState = { } >; }; + closePositionOrderFee: string; }; export type TradePreviewResult = { diff --git a/state/helpers.ts b/state/helpers.ts index 651e8edd06..80d2180f44 100644 --- a/state/helpers.ts +++ b/state/helpers.ts @@ -1,6 +1,6 @@ import Wei, { wei } from '@synthetixio/wei'; -import { FuturesAccountType } from 'queries/futures/types'; +import { FuturesAccountType } from 'sdk/types/futures'; // Redux recommends that values stored in state are serializable // (Generally for diffing, performance and persistence reasons). diff --git a/state/wallet/actions.ts b/state/wallet/actions.ts index 6902b096d2..c942099aff 100644 --- a/state/wallet/actions.ts +++ b/state/wallet/actions.ts @@ -1,6 +1,5 @@ import { createAsyncThunk } from '@reduxjs/toolkit'; import * as Sentry from '@sentry/browser'; -import { NetworkId } from '@synthetixio/contracts-interface'; import { ethers } from 'ethers'; import { fetchBalances } from 'state/balances/actions'; @@ -8,14 +7,6 @@ import type { ThunkConfig } from 'state/types'; import { setWalletAddress } from './reducer'; -export const resetNetwork = createAsyncThunk( - 'wallet/resetNetwork', - async (networkId, { dispatch }) => { - dispatch(fetchBalances()); - return networkId; - } -); - export const resetWalletAddress = createAsyncThunk( 'wallet/resetWalletAddress', async (walletAddress, { dispatch }) => { diff --git a/state/wallet/reducer.ts b/state/wallet/reducer.ts index 7c07d3b36e..2ad8169bf5 100644 --- a/state/wallet/reducer.ts +++ b/state/wallet/reducer.ts @@ -1,6 +1,5 @@ import { createSlice } from '@reduxjs/toolkit'; -import { resetNetwork } from './actions'; import { WalletState } from './types'; export const WALLET_INITIAL_STATE: WalletState = { @@ -23,11 +22,6 @@ const walletSlice = createSlice({ state.networkId = undefined; }, }, - extraReducers: (builder) => { - builder.addCase(resetNetwork.fulfilled, (state, action) => { - state.networkId = action.payload; - }); - }, }); export const { setWalletAddress, setNetwork } = walletSlice.actions; diff --git a/state/wallet/selectors.ts b/state/wallet/selectors.ts index dcf5c245d4..1053ec02d2 100644 --- a/state/wallet/selectors.ts +++ b/state/wallet/selectors.ts @@ -1,6 +1,7 @@ import { createSelector } from '@reduxjs/toolkit'; import { DEFAULT_NETWORK_ID } from 'constants/defaults'; +import { getSynthsForNetwork } from 'sdk/data/synths'; import type { RootState } from 'state/store'; const SUPPORTED_NETWORKS = [1, 10, 5, 420]; @@ -38,3 +39,8 @@ export const selectIsMainnet = createSelector( (state: RootState) => state.wallet.networkId, (networkId) => networkId && (networkId === 5 || networkId === 420) ); + +export const selectSynthsMap = createSelector( + (state: RootState) => state.wallet.networkId, + (networkId) => (networkId ? getSynthsForNetwork(networkId) : {}) +); diff --git a/state/wallet/types.ts b/state/wallet/types.ts index b1d713a395..4af50b4feb 100644 --- a/state/wallet/types.ts +++ b/state/wallet/types.ts @@ -1,4 +1,4 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; +import { NetworkId } from 'sdk/types/common'; export type WalletState = { walletAddress?: string; diff --git a/testing/unit/mocks/MockProviders.tsx b/testing/unit/mocks/MockProviders.tsx index 3372146a22..10f248fe4a 100644 --- a/testing/unit/mocks/MockProviders.tsx +++ b/testing/unit/mocks/MockProviders.tsx @@ -1,5 +1,3 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; -import { createQueryContext, SynthetixQueryContextProvider } from '@synthetixio/queries'; import mockRouter from 'next-router-mock'; import { ReactNode } from 'react'; import { QueryClient, QueryClientProvider } from 'react-query'; @@ -10,9 +8,6 @@ import Connector from 'containers/Connector'; import { wagmiClient } from 'containers/Connector/config'; import { themes } from 'styles/theme'; -import { DEFAULT_NETWORK } from '../constants'; -import { mockProvider, MockEthProvider } from './mockEthersProvider'; - jest.mock('@rainbow-me/rainbowkit', () => ({ wallet: { metaMask: () => {}, @@ -57,41 +52,21 @@ const queryClient = new QueryClient({ type Props = { children: ReactNode; - ethProviderOverrides?: MockEthProvider; route?: string; }; process.env.GIT_HASH_ID = '12345'; -export const SynthetixProvider = ({ children, ethProviderOverrides }: Props) => { - const mockedProvider = mockProvider(ethProviderOverrides); - - return ( - - {children} - - ); -}; - -const MockProviders = ({ children, ethProviderOverrides, route }: Props) => { +const MockProviders = ({ children, route }: Props) => { mockRouter.setCurrentUrl(route || '/'); return ( - - - - {children} - - - + + + {children} + + ); }; diff --git a/translations/en.json b/translations/en.json index c46cc13ed2..eb93064a7b 100644 --- a/translations/en.json +++ b/translations/en.json @@ -34,7 +34,7 @@ }, "networks-switcher": { "chains": "Switch Networks", - "l2": "Switch to L2", + "l2": "Switch to Optimism", "l1": "Switch to Layer 1", "optimistic-gateway": "Optimistic L2 Gateway", "optimistic-etherscan": "Optimistic Etherscan", @@ -183,7 +183,7 @@ "disclaimer": "* These statistics are approximations. User experience will vary.", "cta-buttons": { "learn-more": "Learn more", - "switch-l2": "Switch to L2", + "switch-l2": "Switch to Optimism", "switch-networks": "Switch Networks" }, "markets": { diff --git a/typings/window.d.ts b/typings/window.d.ts index 0cd373c63e..477aa44fc9 100644 --- a/typings/window.d.ts +++ b/typings/window.d.ts @@ -1,6 +1,7 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; import { ethers } from 'ethers'; +import { NetworkId } from 'sdk/types/common'; + declare global { interface Window { web3?: { diff --git a/utils/balances.ts b/utils/balances.ts index 518d2de0c8..30fbcf58fa 100644 --- a/utils/balances.ts +++ b/utils/balances.ts @@ -1,57 +1,11 @@ import Wei, { wei } from '@synthetixio/wei'; -import { ethers } from 'ethers'; import { orderBy } from 'lodash'; -import get from 'lodash/get'; import { notNill } from 'queries/synths/utils'; import { SynthBalance, TokenBalances } from 'sdk/types/tokens'; import { BalancesActionReturn } from 'state/balances/types'; -import { CurrencyKey } from '../constants/currency'; -import { isSynth } from './currencies'; - -type BalancesMap = Record; - -type SynthBalances = { - synths: { - balances: BalancesMap; - }; -}; - -type Balances = SynthBalances & BalancesMap; - -const getSynthBalancePath = (currencyKey: CurrencyKey, field: string) => [ - 'synths', - 'balances', - currencyKey, - field, -]; - -// crypto appears as lowercased in balances -const getCryptoCurrencyBalancePath = (currencyKey: CurrencyKey, field: string) => [ - currencyKey.toLowerCase(), - field, -]; - -export const getCurrencyKeyBalance = (balances: Balances, currencyKey: CurrencyKey): number => - isSynth(currencyKey) - ? get(balances, getSynthBalancePath(currencyKey, 'balance')) - : get(balances, getCryptoCurrencyBalancePath(currencyKey, 'balance')); - -export const getCurrencyKeyUSDBalance = (balances: Balances, currencyKey: CurrencyKey): number => - isSynth(currencyKey) - ? get(balances, getSynthBalancePath(currencyKey, 'usdBalance')) - : get(balances, getCryptoCurrencyBalancePath(currencyKey, 'usdBalance')); - -export const getCurrencyKeyUSDBalanceBN = ( - balances: Balances, - currencyKey: CurrencyKey -): ethers.BigNumber => - isSynth(currencyKey) - ? get(balances, getSynthBalancePath(currencyKey, 'balanceBN')) - : get(balances, getCryptoCurrencyBalancePath(currencyKey, 'balanceBN')); - -export const sortWei = (a: Wei, b: Wei, order: 'descending' | 'ascending'): number => { +export const sortWei = (a: Wei, b: Wei, order: 'descending' | 'ascending') => { const diff = order === 'ascending' ? a.sub(b) : b.sub(a); if (diff.gt(0)) { diff --git a/utils/currencies.ts b/utils/currencies.ts index f56a22a1c3..2c8ccdc74f 100644 --- a/utils/currencies.ts +++ b/utils/currencies.ts @@ -1,9 +1,7 @@ -import { Rates, Token } from '@synthetixio/queries'; -import { wei } from '@synthetixio/wei'; +import Wei, { wei } from '@synthetixio/wei'; import { CurrencyKey, - Synths, CRYPTO_CURRENCY_MAP, FIAT_SYNTHS, ETH_ADDRESS, @@ -14,7 +12,6 @@ import { Price, Prices } from 'sdk/types/prices'; import { PriceResponse } from '../queries/coingecko/types'; -export const isSynth = (currencyKey: CurrencyKey) => !!Synths[currencyKey]; export const isCryptoCurrency = (currencyKey: CurrencyKey) => !!CRYPTO_CURRENCY_MAP[currencyKey]; export const isFiatCurrency = (currencyKey: CurrencyKey) => FIAT_SYNTHS.has(currencyKey); @@ -28,60 +25,36 @@ export const assetToSynth = (currencyKey: CurrencyKey) => `s${currencyKey}`; export const iStandardSynth = (currencyKey: CurrencyKey) => currencyKey.startsWith('s'); export const synthToContractName = (currencyKey: CurrencyKey) => `Synth${currencyKey}`; +type Rates = Record; + +const ADDITIONAL_MARKETS = new Set([ + FuturesMarketKey.sAPEPERP, + FuturesMarketKey.sDYDXPERP, + FuturesMarketKey.sXAUPERP, + FuturesMarketKey.sXAGPERP, +]); + export const getExchangeRatesForCurrencies = ( - rates: Rates | null | undefined, - base: string | null | undefined, - quote: string | null | undefined -) => - rates == null || - base == null || - quote == null || - rates[base] === undefined || - rates[quote] === undefined - ? 0 - : rates[base].toNumber() * (1 / rates[quote].toNumber()); - -export const newGetExchangeRatesForCurrencies = ( rates: Rates | null, - base: CurrencyKey | FuturesMarketKey | string | null, + base: CurrencyKey | FuturesMarketKey | string, quote: CurrencyKey | FuturesMarketKey | null ) => { - base = new Set([ - FuturesMarketKey.sAPEPERP, - FuturesMarketKey.sDYDXPERP, - FuturesMarketKey.sXAUPERP, - FuturesMarketKey.sXAGPERP, - ]).has(base as FuturesMarketKey) - ? synthToAsset(base as CurrencyKey) - : base; - return rates == null || - base == null || - quote == null || - rates[base] === undefined || - rates[quote] === undefined + base = ADDITIONAL_MARKETS.has(base) ? synthToAsset(base) : base; + return !rates || !base || !quote || !rates[base] || !rates[quote] ? wei(0) : rates[base].div(rates[quote]); }; export const getPricesForCurrencies = ( rates: Prices | null, - base: CurrencyKey | FuturesMarketKey | string | null, + base: CurrencyKey | FuturesMarketKey | string, quote: CurrencyKey | FuturesMarketKey | null ): Price => { - base = new Set([ - FuturesMarketKey.sAPEPERP, - FuturesMarketKey.sDYDXPERP, - FuturesMarketKey.sXAUPERP, - FuturesMarketKey.sXAGPERP, - ]).has(base as FuturesMarketKey) - ? synthToAsset(base as CurrencyKey) - : base; + base = ADDITIONAL_MARKETS.has(base) ? synthToAsset(base) : base; if (!rates || !base || !quote || !rates[base] || !rates[quote]) { - return { - offChain: wei(0), - onChain: wei(0), - }; + return { offChain: wei(0), onChain: wei(0) }; } + const hasOnChain = !!rates[base]?.onChain && !!rates[quote]?.onChain; const hasOffChain = !!rates[base]?.offChain && !!rates[quote]?.offChain; @@ -91,38 +64,26 @@ export const getPricesForCurrencies = ( }; }; -export const newGetExchangeRatesTupleForCurrencies = ( +export const getExchangeRatesTupleForCurrencies = ( rates: Rates | null, - base: CurrencyKey | FuturesMarketKey | string | null, + base: CurrencyKey | FuturesMarketKey | string, quote: CurrencyKey | FuturesMarketKey | null ) => { - base = new Set([ - FuturesMarketKey.sAPEPERP, - FuturesMarketKey.sDYDXPERP, - FuturesMarketKey.sXAUPERP, - FuturesMarketKey.sXAGPERP, - ]).has(base as FuturesMarketKey) - ? synthToAsset(base as CurrencyKey) - : base; - const baseRate = - rates == null || base == null || rates[base] === undefined ? wei(0) : rates[base]; - const quoteRate = - rates == null || quote == null || rates[quote] === undefined ? wei(0) : rates[quote]; + base = ADDITIONAL_MARKETS.has(base) ? synthToAsset(base) : base; + const baseRate = !rates || !base || !rates[base] ? wei(0) : rates[base]; + const quoteRate = !rates || !quote || !rates[quote] ? wei(0) : rates[quote]; return [baseRate, quoteRate]; }; export const newGetCoinGeckoPricesForCurrencies = ( coinGeckoPrices: PriceResponse | null, - baseCurrencyTokenAddress: Token['address'] | null + baseAddress: string | null ) => { - if (!coinGeckoPrices || !baseCurrencyTokenAddress) { + if (!coinGeckoPrices || !baseAddress) { return wei(0); } - const base = (baseCurrencyTokenAddress === ETH_ADDRESS - ? ETH_COINGECKO_ADDRESS - : baseCurrencyTokenAddress - ).toLowerCase(); + const base = (baseAddress === ETH_ADDRESS ? ETH_COINGECKO_ADDRESS : baseAddress).toLowerCase(); if (!coinGeckoPrices[base]) { return wei(0); diff --git a/utils/formatters/number.ts b/utils/formatters/number.ts index f2b6d42504..60185beed3 100644 --- a/utils/formatters/number.ts +++ b/utils/formatters/number.ts @@ -167,7 +167,7 @@ export const formatCurrency = ( value: WeiSource, options?: FormatCurrencyOptions ) => - isFiatCurrency(currencyKey as CurrencyKey) + isFiatCurrency(currencyKey) ? formatFiatCurrency(value, options) : formatCryptoCurrency(value, options); diff --git a/utils/futures.ts b/utils/futures.ts index 8b3bf89fdf..720258d851 100644 --- a/utils/futures.ts +++ b/utils/futures.ts @@ -1,7 +1,5 @@ -import { Synth } from '@synthetixio/contracts-interface'; import Wei, { wei } from '@synthetixio/wei'; import { TFunction } from 'i18next'; -import { Dictionary } from 'lodash'; import { FuturesMarket, @@ -40,11 +38,7 @@ export const getDisplayAsset = (asset: string | null) => { return asset ? (asset[0] === 's' ? asset.slice(1) : asset) : null; }; -export const getSynthDescription = ( - synth: FuturesMarketAsset, - synthsMap: Dictionary, - t: TFunction -) => { +export const getSynthDescription = (synth: FuturesMarketAsset, t: TFunction) => { const assetDisplayName = AssetDisplayByAsset[synth]; return t('common.currency.futures-market-short-name', { currencyName: assetDisplayName, @@ -256,16 +250,14 @@ export const updatePositionUpnl = ( const thisPositionHistory = positionHistory.find( ({ isOpen, asset }) => isOpen && asset === positionDetails.asset ); + if (!thisPositionHistory || !position || !offChainPrice) return deserializedPositionDetails; - const pnl = - !!thisPositionHistory && !!position && !!offChainPrice - ? position.size.mul( - thisPositionHistory.avgEntryPrice - .sub(offChainPrice) - .mul(position.side === PositionSide.LONG ? -1 : 1) - ) - : undefined; - const pnlPct = pnl?.div(position?.initialMargin); + const pnl = position.size.mul( + thisPositionHistory.avgEntryPrice + .sub(offChainPrice) + .mul(position.side === PositionSide.LONG ? -1 : 1) + ); + const pnlPct = pnl.div(position.initialMargin.add(thisPositionHistory.netTransfers)); return { ...deserializedPositionDetails, @@ -610,6 +602,7 @@ export const serializeTrades = (trades: FuturesTrade[]): FuturesTrade[] positionSize: t.positionSize.toString(), pnl: t.pnl.toString(), feesPaid: t.feesPaid.toString(), + keeperFeesPaid: t.keeperFeesPaid.toString(), })); }; @@ -622,6 +615,7 @@ export const unserializeTrades = (trades: FuturesTrade[]): FuturesTrade< positionSize: wei(t.positionSize), pnl: wei(t.pnl), feesPaid: wei(t.feesPaid), + keeperFeesPaid: wei(t.keeperFeesPaid), })); }; diff --git a/utils/infura.ts b/utils/infura.ts index e09de0c82a..9080ab9ed0 100644 --- a/utils/infura.ts +++ b/utils/infura.ts @@ -1,4 +1,4 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; +import { NetworkId } from 'sdk/types/common'; export const GWEI_UNIT = 1000000000; diff --git a/utils/network.ts b/utils/network.ts index 2251d06dcf..fa1f7f5dd3 100644 --- a/utils/network.ts +++ b/utils/network.ts @@ -1,9 +1,7 @@ import detectEthereumProvider from '@metamask/detect-provider'; -import { NetworkId } from '@synthetixio/contracts-interface'; import loadProvider from '@synthetixio/providers'; -import { GasPrice } from '@synthetixio/queries'; import Wei, { wei } from '@synthetixio/wei'; -import { providers } from 'ethers'; +import { ethers, providers } from 'ethers'; import { DEFAULT_GAS_BUFFER, DEFAULT_NETWORK_ID } from 'constants/defaults'; import { @@ -13,6 +11,8 @@ import { SUPPORTED_NETWORKS, BLAST_NETWORK_LOOKUP, } from 'constants/network'; +import { NetworkId } from 'sdk/types/common'; +import { GasPrice } from 'state/app/types'; import logError from './logError'; @@ -21,11 +21,13 @@ type EthereumProvider = { chainId: string; }; +export const staticMainnetProvider = new ethers.providers.InfuraProvider(); + export function isSupportedNetworkId(id: NetworkId): boolean { return SUPPORTED_NETWORKS.includes(id); } -export async function getDefaultNetworkId(walletConnected: boolean = true): Promise { +export async function getDefaultNetworkId(walletConnected = true) { try { if (walletConnected && window.ethereum) { const provider = (await detectEthereumProvider()) as EthereumProvider; @@ -100,15 +102,13 @@ export const getTransactionPrice = ( ) => { if (!gasPrice || !gasLimit || !ethPrice) return null; const totalGasPrice = getTotalGasPrice(gasPrice); - const extraLayer1Fees = optimismLayerOneFee; const gasPriceCost = totalGasPrice.mul(wei(gasLimit, GWEI_DECIMALS)).mul(ethPrice); - const l1Cost = ethPrice.mul(extraLayer1Fees || 0); - const txPrice = gasPriceCost.add(l1Cost); - return txPrice; + const l1Cost = ethPrice.mul(optimismLayerOneFee || 0); + return gasPriceCost.add(l1Cost); }; export const normalizeGasLimit = (gasLimit: number) => gasLimit + DEFAULT_GAS_BUFFER; export const gasPriceInWei = (gasPrice: number) => Math.ceil(gasPrice * GWEI_UNIT); // 🤔 sometimes a float on kovan -export const getIsOVM = (networkId: number): boolean => [10, 69, 420].includes(networkId); +export const getIsOVM = (networkId: number) => [10, 69, 420].includes(networkId);