diff --git a/.envrc b/.envrc deleted file mode 100644 index 6b7a7d5cc07..00000000000 --- a/.envrc +++ /dev/null @@ -1,4 +0,0 @@ -watch_file flake.nix -watch_file flake.lock -mkdir -p .direnv -eval "$(nix print-dev-env --profile "$(direnv_layout_dir)/flake-profile")" diff --git a/Deploys.csv b/Deploys.csv deleted file mode 100644 index 99efafd7469..00000000000 --- a/Deploys.csv +++ /dev/null @@ -1,2 +0,0 @@ -name,address -veMOBI SNX,0x0812f6de916667C5aa820E757704c4ac69159529 \ No newline at end of file diff --git a/SNX_Addresses.txt b/SNX_Addresses.txt deleted file mode 100644 index 3fae61d8819..00000000000 --- a/SNX_Addresses.txt +++ /dev/null @@ -1,4 +0,0 @@ -USDC Optics Pool - SNX: 0xC8B86C28A0026eA72b7624FA3C912ad77F1D5fe0 -Solana Allbridge - SNX: 0xedD2c93858507A64B29a4aF0591bd2c571851829 -USDC POS - SNX: 0x3804da7A53FD810CD7f5bfcD1166eA2E0E9e9aE0 -USDC Moss - SNX: 0xd4A3debc32bdFE07b92c7D67297deCD00FC4236b \ No newline at end of file diff --git a/TestPoolInfo.txt b/TestPoolInfo.txt deleted file mode 100644 index 1ebdaead76d..00000000000 --- a/TestPoolInfo.txt +++ /dev/null @@ -1,91 +0,0 @@ -Test Pool 1 - -Transaction sent: 0x61657b0b74d62d6293ea6b84217e7dd821e0d05ba1984d3ae52235f54ee05713 - Gas price: 0.5 gwei Gas limit: 8000000 Nonce: 41 - ERC20Mock.constructor confirmed Block: 7014288 Gas used: 500215 (6.25%) - ERC20Mock deployed at: 0x2AaF20d89277BF024F463749045964D7e7d3A774 - -Transaction sent: 0x6a12d33f99f2a00f4fe40160a0126d7557ff9217e344db3643cff725e5a3f870 - Gas price: 0.5 gwei Gas limit: 8000000 Nonce: 42 - ERC20Mock.constructor confirmed Block: 7014292 Gas used: 500215 (6.25%) - ERC20Mock deployed at: 0x3551d53C9CF91E222D9579A1Ac4B44117E8Ec609 - -Transaction sent: 0x296f67cc093db17349cfaa79f94c0a724424e97c0700ef259abc4727a848f7ac - Gas price: 0.5 gwei Gas limit: 8000000 Nonce: 43 - MathUtils.constructor confirmed Block: 7014293 Gas used: 114689 (1.43%) - MathUtils deployed at: 0xa986725a7f0c6c76BB36531A29748839819da100 - -Transaction sent: 0xc4c08290c02525ca7966ef3cd6fd3dbc6db8e451b8219e1ac7a390c9444b3620 - Gas price: 0.5 gwei Gas limit: 8000000 Nonce: 44 - SwapUtils.constructor confirmed Block: 7014296 Gas used: 4067058 (50.84%) - SwapUtils deployed at: 0xF9B55CA1F693EC1e19F2dacF204642C99278C2C4 - -Transaction sent: 0x6931fbaf53a1b665892451f6c20edeabb11bb6da9b70f78b9dbcaa570ffb01ca - Gas price: 0.5 gwei Gas limit: 8000000 Nonce: 45 - Swap.constructor confirmed Block: 7014300 Gas used: 3527763 (44.10%) - Swap deployed at: 0xaAB4a154EE836fcDaa706da7BE3Cd36d116dcF84 - -Deployed at: -Swap: 0xaAB4a154EE836fcDaa706da7BE3Cd36d116dcF84 -Transaction sent: 0x413d4665d737332730daf720f4153835be5e3f5ce8c61b75a9d626e8fdb63cf1 - Gas price: 0.5 gwei Gas limit: 8000000 Nonce: 46 - ERC20Mock.approve confirmed Block: 7014302 Gas used: 43814 (0.55%) - -Transaction sent: 0x4db57f22264f9574cae052954b7cf6f1a8602bf89a1650338b0063f6d5b27acb - Gas price: 0.5 gwei Gas limit: 8000000 Nonce: 47 - ERC20Mock.approve confirmed Block: 7014303 Gas used: 43814 (0.55%) - -Transaction sent: 0x6c8cfd2f965a7d71d13224d05b018dafc49ace9553f6b50916372a7f7aaa9dd5 - Gas price: 0.5 gwei Gas limit: 8000000 Nonce: 48 - Swap.addLiquidity confirmed Block: 7014305 Gas used: 231608 (2.90%) - - -Pool 2 (3 tokens) - -Transaction sent: 0x5fa816e777e4dab3630f9658d49071196b2244311dd3acb14e7b04c66da4292e - Gas price: 0.5 gwei Gas limit: 8000000 Nonce: 58 - ERC20Mock.constructor confirmed Block: 7014406 Gas used: 500215 (6.25%) - ERC20Mock deployed at: 0x7588110A070987ea0347Cf788226c28d1476d641 - -Transaction sent: 0x97e701e26a556d459bd88fe98964a4b407eabd16d204286e6c8d6533dcc565c4 - Gas price: 0.5 gwei Gas limit: 8000000 Nonce: 59 - ERC20Mock.constructor confirmed Block: 7014407 Gas used: 500215 (6.25%) - ERC20Mock deployed at: 0x17Ec8dab839a9880D656c3cEF40cf4038657d168 - -Transaction sent: 0x3c92ba6d8af3ffe1dc8323dee12bbc128327906795f03479084c8effeb88826e - Gas price: 0.5 gwei Gas limit: 8000000 Nonce: 60 - ERC20Mock.constructor confirmed Block: 7014410 Gas used: 500215 (6.25%) - ERC20Mock deployed at: 0xCC531BfBA46cA251D3D9f3aCc37ABD5DCF3ed0B3 - -Transaction sent: 0x7ad75c4cb7a2540bdc5186db45adefcda59e23b2c27bd037de735a984f5372d1 - Gas price: 0.5 gwei Gas limit: 8000000 Nonce: 61 - MathUtils.constructor confirmed Block: 7014412 Gas used: 114689 (1.43%) - MathUtils deployed at: 0xbEA8Cf786865D8d72068284e70d92d500D32F17E - -Transaction sent: 0x3bcf4fb5bcc179db270d5acf189ce95eedfa946f6f2e64d67c21b28e972aeb1a - Gas price: 0.5 gwei Gas limit: 8000000 Nonce: 62 - SwapUtils.constructor confirmed Block: 7014413 Gas used: 4067118 (50.84%) - SwapUtils deployed at: 0x523b7553421987D008Ef70aC56ecCD9883979FAC - -Transaction sent: 0xf711eda2de779c9a659a31570768f73fe23e0c752551f0b7bde3cefc1db78083 - Gas price: 0.5 gwei Gas limit: 8000000 Nonce: 63 - Swap.constructor confirmed Block: 7014414 Gas used: 3591225 (44.89%) - Swap deployed at: 0xFB80520416685420751B2CD8E2c305aCbd5F756E - -Deployed at: -Swap: 0xFB80520416685420751B2CD8E2c305aCbd5F756E -Transaction sent: 0xda36fab594b00fc66278282340490e67d84ac518793a1cdbda82be0c935bf7f8 - Gas price: 0.5 gwei Gas limit: 8000000 Nonce: 64 - ERC20Mock.approve confirmed Block: 7014415 Gas used: 43814 (0.55%) - -Transaction sent: 0x56ce17c1c6c5cdbdcd1af73b06d479165825ca29c3dd3c9d7dd48e619b273034 - Gas price: 0.5 gwei Gas limit: 8000000 Nonce: 65 - ERC20Mock.approve confirmed Block: 7014416 Gas used: 43814 (0.55%) - -Transaction sent: 0xfd8c22ce86a20356728545e34352f6d37321efd5141cc97307a3831d42069a8f - Gas price: 0.5 gwei Gas limit: 8000000 Nonce: 66 - ERC20Mock.approve confirmed Block: 7014419 Gas used: 43814 (0.55%) - -Transaction sent: 0xf8f9ae0ba9313193779d30a95c189f1848915794db557647b959b54487ce595b - Gas price: 0.5 gwei Gas limit: 8000000 Nonce: 67 - Swap.addLiquidity confirmed Block: 7014425 Gas used: 282252 (3.53%) \ No newline at end of file diff --git a/craco.config.js b/craco.config.js index 6a0a33740b2..8d6dbae6919 100644 --- a/craco.config.js +++ b/craco.config.js @@ -1,6 +1,3 @@ - -const now = Math.floor(new Date().getTime() / 1000) - module.exports = { webpack: { plugins: { diff --git a/flake.nix b/flake.nix deleted file mode 100644 index 69827df7b49..00000000000 --- a/flake.nix +++ /dev/null @@ -1,15 +0,0 @@ -{ - description = "Mobius frontend interface"; - - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - flake-utils.url = "github:numtide/flake-utils"; - }; - - outputs = { self, nixpkgs, flake-utils }: - let systems = [ "x86_64-darwin" "x86_64-linux" ]; - - in flake-utils.lib.eachSystem systems (system: - let pkgs = nixpkgs.legacyPackages.${system}; - in { devShell = import ./shell.nix { inherit pkgs; }; }); -} diff --git a/netlify.toml b/netlify.toml deleted file mode 100644 index a592cf9477d..00000000000 --- a/netlify.toml +++ /dev/null @@ -1,2 +0,0 @@ -[build.environment] - NODE_OPTIONS = "--max_old_space_size=4096" \ No newline at end of file diff --git a/package.json b/package.json index fca8b1a2c75..ba662515ec1 100644 --- a/package.json +++ b/package.json @@ -9,10 +9,8 @@ "@reach/portal": "^0.15.0", "@reduxjs/toolkit": "^1.5.1", "@typechain/ethers-v5": "^7.0.1", - "@types/jest": "^26.0.23", "@types/ledgerhq__hw-transport-webusb": "^4.70.1", "@types/lodash": "^4.14.170", - "@types/luxon": "^2.0.7", "@types/multicodec": "^2.1.0", "@types/node": "^14.14.37", "@types/react": "^17.0.11", @@ -125,13 +123,19 @@ "@ethersproject/strings": "^5.3.0", "@ethersproject/units": "^5.3.0", "@sentry/react": "^6.9.0", + "@types/jest": "^27.4.1", + "@types/lodash.mapvalues": "^4.6.6", + "@types/luxon": "^2.0.9", "@types/react-date-range": "^1.1.8", "@types/react-vis": "^1.11.7", "@ubeswap/sdk": "^2.1.0", + "@ubeswap/token-math": "^4.4.4", "axios": "^0.21.4", "bignumber.js": "^9.0.1", "graphql": "^15.7.1", + "jsbi": "^4.1.0", "lodash": "^4.17.21", + "lodash.mapvalues": "^4.6.0", "randombytes": "^2.1.0", "react-circular-progressbar": "^2.0.4", "react-countdown": "^2.3.2", diff --git a/shell.nix b/shell.nix deleted file mode 100644 index 8f522c691ab..00000000000 --- a/shell.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ pkgs }: -pkgs.mkShell { - buildInputs = with pkgs; [ - nodejs-14_x - (yarn.override { nodejs = nodejs-14_x; }) - ]; -} diff --git a/src/components/CurrencyInputPanel/index.tsx b/src/components/CurrencyInputPanel/index.tsx index d5da3476b79..3b225d95f19 100644 --- a/src/components/CurrencyInputPanel/index.tsx +++ b/src/components/CurrencyInputPanel/index.tsx @@ -1,5 +1,5 @@ -import { Pair, Token } from '@ubeswap/sdk' import { useColor } from 'hooks/useColor' +import { Token, TokenAmount } from 'lib/token-utils' import { darken } from 'polished' import React, { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -12,7 +12,6 @@ import { useIsDarkMode } from '../../state/user/hooks' import { useTokenBalance } from '../../state/wallet/hooks' import { TYPE } from '../../theme' import CurrencyLogo from '../CurrencyLogo' -import DoubleCurrencyLogo from '../DoubleLogo' import { Input as NumericalInput } from '../NumericalInput' import { RowBetween } from '../Row' import CurrencySearchModal from '../SearchModal/CurrencySearchModal' @@ -47,7 +46,6 @@ const CurrencySelect = styled.button<{ walletConnected: boolean bgColor: any isDarkMode: boolean - pair: boolean }>` display: flex; align-items: center; @@ -64,7 +62,7 @@ const CurrencySelect = styled.button<{ cursor: pointer; user-select: none; border: none; - width: ${({ pair }) => (pair ? '14rem' : '10rem')}; + width: 10rem; height: 2.4rem; padding: 0 8px; @@ -139,14 +137,13 @@ export enum TokenType { interface CurrencyInputPanelProps { value: string onUserInput: (value: string) => void - onMax?: () => void + onMax?: (max?: TokenAmount) => void showMaxButton: boolean label?: string onCurrencySelect?: (currency: Token) => void currency?: Token | null disableCurrencySelect?: boolean hideBalance?: boolean - pair?: Pair | null hideInput?: boolean disableInput?: boolean otherCurrency?: Token | null @@ -165,7 +162,6 @@ export default function CurrencyInputPanel({ disableCurrencySelect = false, hideBalance = false, disableInput = false, - pair = null, // used for double token logo hideInput = false, otherCurrency, id, @@ -197,7 +193,7 @@ export default function CurrencyInputPanel({ {connected && ( (onMax ? onMax(selectedCurrencyBalance) : null)} color={theme.text2} fontWeight={500} fontSize={14} @@ -217,7 +213,6 @@ export default function CurrencyInputPanel({ bgColor={tokenSelectBackground} selected={!!currency} walletConnected={connected} - pair={!!pair} className="open-currency-select-button" onClick={() => { if (!disableCurrencySelect) { @@ -226,24 +221,15 @@ export default function CurrencyInputPanel({ }} > - {pair ? ( - - ) : currency ? ( - - ) : null} - {pair ? ( - - {pair?.token0.symbol}:{pair?.token1.symbol} - - ) : ( - - {(currency && currency.symbol && currency.symbol.length > 20 - ? currency.symbol.slice(0, 4) + - '...' + - currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length) - : currency?.symbol) || t('Select Token')} - - )} + {currency ? : null} + + {(currency && currency.symbol && currency.symbol.length > 20 + ? currency.symbol.slice(0, 4) + + '...' + + currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length) + : currency?.symbol) || t('Select Token')} + + {!disableCurrencySelect && } diff --git a/src/components/CurrencyLogo/index.tsx b/src/components/CurrencyLogo/index.tsx index 7c71b04f0ff..3d1accbede5 100644 --- a/src/components/CurrencyLogo/index.tsx +++ b/src/components/CurrencyLogo/index.tsx @@ -1,9 +1,8 @@ -import { Token } from '@ubeswap/sdk' +import { Token } from 'lib/token-utils' import React, { useMemo } from 'react' import styled from 'styled-components' import useHttpLocations from '../../hooks/useHttpLocations' -import { WrappedTokenInfo } from '../../state/lists/hooks' import Logo from '../Logo' const StyledLogo = styled(Logo)<{ size: string }>` @@ -23,15 +22,11 @@ export default function CurrencyLogo({ size?: string style?: React.CSSProperties }) { - const uriLocations = useHttpLocations(currency instanceof WrappedTokenInfo ? currency.logoURI : undefined) + const uriLocations = useHttpLocations(currency?.icon ?? undefined) const srcs: string[] = useMemo(() => { - if (currency instanceof Token) { - if (currency instanceof WrappedTokenInfo) { - return [...uriLocations, currency.logoURI ?? currency.address] - } - - return [] + if (currency) { + return [...uriLocations, currency.icon ?? currency.address] } return [] }, [currency, uriLocations]) diff --git a/src/components/CurrencyPoolLogo/index.tsx b/src/components/CurrencyPoolLogo/index.tsx index 2f76f69dc01..25b1ba410aa 100644 --- a/src/components/CurrencyPoolLogo/index.tsx +++ b/src/components/CurrencyPoolLogo/index.tsx @@ -1,4 +1,4 @@ -import { Token } from '@ubeswap/sdk' +import { Token } from 'lib/token-utils' import React from 'react' import styled from 'styled-components' diff --git a/src/components/DoubleLogo/index.tsx b/src/components/DoubleLogo/index.tsx index 466d2a32afc..ca0152ee226 100644 --- a/src/components/DoubleLogo/index.tsx +++ b/src/components/DoubleLogo/index.tsx @@ -1,4 +1,4 @@ -import { Token } from '@ubeswap/sdk' +import { Token } from 'lib/token-utils' import React from 'react' import styled from 'styled-components' diff --git a/src/components/FormattedCurrencyAmount/index.tsx b/src/components/FormattedCurrencyAmount/index.tsx index ade4ff8f505..ba53952f838 100644 --- a/src/components/FormattedCurrencyAmount/index.tsx +++ b/src/components/FormattedCurrencyAmount/index.tsx @@ -1,4 +1,5 @@ -import { Fraction, JSBI, TokenAmount } from '@ubeswap/sdk' +import JSBI from 'jsbi' +import { Fraction, TokenAmount } from 'lib/token-utils' import React from 'react' const CURRENCY_AMOUNT_MIN = new Fraction(JSBI.BigInt(1), JSBI.BigInt(1000000)) diff --git a/src/components/Header/URLWarning.tsx b/src/components/Header/URLWarning.tsx index b1a0fc20ca8..9b2849ec0e6 100644 --- a/src/components/Header/URLWarning.tsx +++ b/src/components/Header/URLWarning.tsx @@ -1,4 +1,4 @@ -import { ChainId } from '@ubeswap/sdk' +import { ChainId } from 'lib/token-utils' import React from 'react' import { isMobile } from 'react-device-detect' import { AlertTriangle, X } from 'react-feather' @@ -25,9 +25,9 @@ export const StyledClose = styled(X)` ` const appURL: { [id in ChainId]: string } = { - [ChainId.MAINNET]: 'mobius.money', - [ChainId.ALFAJORES]: 'app-alfajores.mobius.money', - [ChainId.BAKLAVA]: 'app-baklava.mobius.money', + [ChainId.Mainnet]: 'mobius.money', + [ChainId.Alfajores]: 'app-alfajores.mobius.money', + [ChainId.Baklava]: 'app-baklava.mobius.money', } export default function URLWarning() { diff --git a/src/components/Header/UbeBalanceContent.tsx b/src/components/Header/UbeBalanceContent.tsx index 95c0207bbd9..eeb40146a2b 100644 --- a/src/components/Header/UbeBalanceContent.tsx +++ b/src/components/Header/UbeBalanceContent.tsx @@ -1,10 +1,9 @@ import Loader from 'components/Loader' import QuestionHelper from 'components/QuestionHelper' -import { useMobi } from 'hooks/Tokens' import React from 'react' import { X } from 'react-feather' +import { useMobiPrice } from 'state/application/hooks' import styled from 'styled-components' -import { useCUSDPrice } from 'utils/useCUSDPrice' import tokenLogo from '../../assets/images/MOBI-200.png' import { CHAIN, MOBI } from '../../constants' @@ -44,8 +43,7 @@ export default function UbeBalanceContent({ setShowUbeBalanceModal }: { setShowU const ube = MOBI[CHAIN] const total = useAggregateUbeBalance() - const mobi = useMobi() - const mobiprice = useCUSDPrice(mobi) + const mobiprice = useMobiPrice() const ret = useCirculatingSupply() return ( diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx index 9cd1153ae08..5b900471a07 100644 --- a/src/components/Header/index.tsx +++ b/src/components/Header/index.tsx @@ -1,7 +1,7 @@ -import { CELO, TokenAmount } from '@ubeswap/sdk' import { CardNoise } from 'components/earn/styled' import Modal from 'components/Modal' import usePrevious from 'hooks/usePrevious' +import { TokenAmount } from 'lib/token-utils' import { darken } from 'polished' import React, { useState } from 'react' import { isMobile } from 'react-device-detect' @@ -17,6 +17,7 @@ import { CountUp } from 'use-count-up' import Logo from '../../assets/svg/mobius.svg' import { CHAIN } from '../../constants' +import { CELO } from '../../constants/tokens' import { useWeb3Context } from '../../hooks' import useTheme from '../../hooks/useTheme' import { useDarkModeManager } from '../../state/user/hooks' diff --git a/src/components/Header/useCirculatingSupply.ts b/src/components/Header/useCirculatingSupply.ts index 829f5fa5cab..033122fa28a 100644 --- a/src/components/Header/useCirculatingSupply.ts +++ b/src/components/Header/useCirculatingSupply.ts @@ -1,7 +1,8 @@ import { BigNumber } from '@ethersproject/bignumber' -import { JSBI, Percent, TokenAmount } from '@ubeswap/sdk' import { MOBI } from 'constants/tokens' import { useMobiContract } from 'hooks/useContract' +import JSBI from 'jsbi' +import { TokenAmount } from 'lib/token-utils' import { useEffect, useState } from 'react' import { useSingleContractMultipleData } from 'state/multicall/hooks' @@ -21,7 +22,7 @@ const nonCirculatingAddresses = { /** * Fetches the circulating supply */ -export const useCirculatingSupply = (): { supply: TokenAmount; staked: Percent } | undefined => { +export const useCirculatingSupply = (): { supply: TokenAmount; staked: TokenAmount } | undefined => { const mobi = MOBI[CHAIN] const mobiContract = useMobiContract() diff --git a/src/components/NavigationTabs/index.tsx b/src/components/NavigationTabs/index.tsx index 108f965f269..fb73288f728 100644 --- a/src/components/NavigationTabs/index.tsx +++ b/src/components/NavigationTabs/index.tsx @@ -51,6 +51,7 @@ const StyledArrowLeft = styled(ArrowLeft)` color: ${({ theme }) => theme.text1}; ` +// TODO: does this do anything export function SwapPoolTabs({ active }: { active: 'swap' | 'mento' | 'pool' | 'send' }) { const { t } = useTranslation() return ( diff --git a/src/components/PoolSearchModal/CurrencyList.tsx b/src/components/PoolSearchModal/CurrencyList.tsx index 2475254904c..52881f1f264 100644 --- a/src/components/PoolSearchModal/CurrencyList.tsx +++ b/src/components/PoolSearchModal/CurrencyList.tsx @@ -1,67 +1,15 @@ -import { currencyEquals, Token } from '@ubeswap/sdk' +import { Token } from 'lib/token-utils' import React, { CSSProperties, MutableRefObject, useCallback } from 'react' import { FixedSizeList } from 'react-window' import { Text } from 'rebass' -import styled from 'styled-components' -import checkedLogo from '../../assets/svg/mobius.svg' -import { WrappedTokenInfo } from '../../state/lists/hooks' import Column from '../Column' import { MenuItem } from '../SearchModal/styleds' -import { MouseoverTooltip } from '../Tooltip' function currencyKey(currency: Token): string { return currency instanceof Token ? currency.address : '' } -const Tag = styled.div` - background-color: ${({ theme }) => theme.bg3}; - color: ${({ theme }) => theme.text2}; - font-size: 14px; - border-radius: 4px; - padding: 0.25rem 0.3rem 0.25rem 0.3rem; - max-width: 6rem; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - justify-self: flex-end; - margin-right: 4px; -` - -const TagContainer = styled.div` - display: flex; - justify-content: flex-end; -` - -function TokenTags({ currency }: { currency: Token }) { - if (!(currency instanceof WrappedTokenInfo)) { - return - } - - const tags = currency.tags - if (!tags || tags.length === 0) return - - const tag = tags[0] - - return ( - - - {tag.name} - - {tags.length > 1 ? ( - `${name}: ${description}`) - .join('; \n')} - > - ... - - ) : null} - - ) -} - function CurrencyRow({ currency, onSelect, @@ -74,11 +22,6 @@ function CurrencyRow({ style: CSSProperties }) { const key = currencyKey(currency) - if (isSelected) - currency = { - ...currency, - logoURI: checkedLogo, - } as WrappedTokenInfo // only show add or remove buttons if not on selected list return ( @@ -93,7 +36,6 @@ function CurrencyRow({ {currency.symbol} - ) } @@ -121,7 +63,7 @@ export default function CurrencyList({ const Row = useCallback( ({ data, index, style }) => { const currency: Token = data[index] - const isSelected = Boolean(selectedCurrency && currencyEquals(selectedCurrency, currency)) + const isSelected = Boolean(selectedCurrency && selectedCurrency.equals(currency)) const handleSelect = () => onCurrencySelect(currency) // const token = currency diff --git a/src/components/PoolSearchModal/CurrencySearch.tsx b/src/components/PoolSearchModal/CurrencySearch.tsx index 9024df25d59..914dbfe6d9e 100644 --- a/src/components/PoolSearchModal/CurrencySearch.tsx +++ b/src/components/PoolSearchModal/CurrencySearch.tsx @@ -1,13 +1,12 @@ -import { Token } from '@ubeswap/sdk' -import { STATIC_POOL_INFO } from 'constants/StablePools' +import { StablePools } from 'constants/pools' import { useOnClickOutside } from 'hooks/useOnClickOutside' import useTheme from 'hooks/useTheme' import useToggle from 'hooks/useToggle' +import { Token } from 'lib/token-utils' import React, { useCallback, useRef } from 'react' import AutoSizer from 'react-virtualized-auto-sizer' import { FixedSizeList } from 'react-window' import { Text } from 'rebass' -import { WrappedTokenInfo } from 'state/lists/hooks' import styled from 'styled-components' import { CHAIN } from '../../constants' @@ -36,17 +35,7 @@ export function CurrencySearch({ selectedCurrency, onCurrencySelect, onDismiss } // refs for fixed size lists const fixedList = useRef() - const tokensToSelect = STATIC_POOL_INFO[CHAIN].map( - ({ lpToken, name }) => - new WrappedTokenInfo( - { - ...lpToken, - symbol: name, - name, - }, - [] - ) - ) + const tokensToSelect = StablePools[CHAIN].map((s) => s.pool.lpToken) const handleCurrencySelect = useCallback( (currency: Token) => { diff --git a/src/components/PoolSearchModal/CurrencySearchModal.tsx b/src/components/PoolSearchModal/CurrencySearchModal.tsx index b3bee60783f..8f691601744 100644 --- a/src/components/PoolSearchModal/CurrencySearchModal.tsx +++ b/src/components/PoolSearchModal/CurrencySearchModal.tsx @@ -1,4 +1,4 @@ -import { Token } from '@ubeswap/sdk' +import { Token } from 'lib/token-utils' import React, { useCallback } from 'react' import Modal from '../Modal' diff --git a/src/components/SearchModal/CurrencyList.tsx b/src/components/SearchModal/CurrencyList.tsx index 8613ce2dcac..9d1741ff775 100644 --- a/src/components/SearchModal/CurrencyList.tsx +++ b/src/components/SearchModal/CurrencyList.tsx @@ -1,18 +1,17 @@ -import { currencyEquals, JSBI, Token, TokenAmount } from '@ubeswap/sdk' +import JSBI from 'jsbi' +import { Token, TokenAmount } from 'lib/token-utils' import React, { CSSProperties, MutableRefObject, useCallback } from 'react' import { FixedSizeList } from 'react-window' import { Text } from 'rebass' import styled from 'styled-components' import { useWeb3Context } from '../../hooks' -import { WrappedTokenInfo } from '../../state/lists/hooks' import { useTokenBalance } from '../../state/wallet/hooks' import { TYPE } from '../../theme' import Column from '../Column' import CurrencyLogo from '../CurrencyLogo' import Loader from '../Loader' import { RowFixed } from '../Row' -import { MouseoverTooltip } from '../Tooltip' import { MenuItem } from './styleds' function currencyKey(currency: Token): string { @@ -58,33 +57,28 @@ const TagContainer = styled.div` justify-content: flex-end; ` +// TODO: Add this feature back in function TokenTags({ currency }: { currency: Token }) { - if (!(currency instanceof WrappedTokenInfo)) { - return - } - - const tags = currency.tags - if (!tags || tags.length === 0) return - - const tag = tags[0] - - return ( - - - {tag.name} - - {tags.length > 1 ? ( - `${name}: ${description}`) - .join('; \n')} - > - ... - - ) : null} - - ) + const tags = currency.info.extensions + if (!tags) return + + // return ( + // + // + // {tag.name} + // + // {tags.length > 1 ? ( + // `${name}: ${description}`) + // .join('; \n')} + // > + // ... + // + // ) : null} + // + // ) } function CurrencyRow({ @@ -122,7 +116,7 @@ function CurrencyRow({ {currency.name} - + {/* */} {balance ? : connected ? : null} @@ -151,8 +145,8 @@ export default function CurrencyList({ const Row = useCallback( ({ data, index, style }) => { const currency: Token = data[index] - const isSelected = Boolean(selectedCurrency && currencyEquals(selectedCurrency, currency)) - const otherSelected = Boolean(otherCurrency && currencyEquals(otherCurrency, currency)) + const isSelected = Boolean(selectedCurrency && selectedCurrency.equals(currency)) + const otherSelected = Boolean(otherCurrency && otherCurrency.equals(currency)) const handleSelect = () => onCurrencySelect(currency) return ( diff --git a/src/components/SearchModal/CurrencySearch.tsx b/src/components/SearchModal/CurrencySearch.tsx index 0a4c36d6a11..f4835e1622e 100644 --- a/src/components/SearchModal/CurrencySearch.tsx +++ b/src/components/SearchModal/CurrencySearch.tsx @@ -1,7 +1,8 @@ -import { cUSD, Token } from '@ubeswap/sdk' +import { CUSD } from 'constants/tokens' import { useOnClickOutside } from 'hooks/useOnClickOutside' import useTheme from 'hooks/useTheme' import useToggle from 'hooks/useToggle' +import { Token } from 'lib/token-utils' import React, { KeyboardEvent, RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react' import ReactGA from 'react-ga' import { useTranslation } from 'react-i18next' @@ -13,8 +14,7 @@ import { useOpticsV1Tokens, useOpticsV2Tokens } from 'state/openSum/hooks' import styled from 'styled-components' import { CHAIN } from '../../constants' -import { useSwappableTokens } from '../../hooks/Tokens' -import { useTokensTradeable } from '../../state/stake/hooks' +import { useSwappableTokens, useTokensTradeable } from '../../hooks/Tokens' import { CloseIcon, TYPE } from '../../theme' import { isAddress } from '../../utils' import Column from '../Column' @@ -149,7 +149,7 @@ export function CurrencySearch({ if (e.key === 'Enter') { const s = searchQuery.toLowerCase().trim() if (s === 'cusd') { - handleCurrencySelect(cUSD[CHAIN]) + handleCurrencySelect(CUSD[CHAIN]) } else if (filteredSortedTokens.length > 0) { if ( filteredSortedTokens[0].symbol?.toLowerCase() === searchQuery.trim().toLowerCase() || diff --git a/src/components/SearchModal/CurrencySearchModal.tsx b/src/components/SearchModal/CurrencySearchModal.tsx index 016f8c40684..477948765cf 100644 --- a/src/components/SearchModal/CurrencySearchModal.tsx +++ b/src/components/SearchModal/CurrencySearchModal.tsx @@ -1,4 +1,4 @@ -import { Token } from '@ubeswap/sdk' +import { Token } from 'lib/token-utils' import React, { useCallback, useEffect, useState } from 'react' import { useLocation } from 'react-router' diff --git a/src/components/SearchModal/filtering.ts b/src/components/SearchModal/filtering.ts index e1af52a0e91..a6838f9ba5d 100644 --- a/src/components/SearchModal/filtering.ts +++ b/src/components/SearchModal/filtering.ts @@ -1,4 +1,4 @@ -import { Token } from '@ubeswap/sdk' +import { Token } from 'lib/token-utils' import { isAddress } from '../../utils' diff --git a/src/components/SearchModal/sorting.ts b/src/components/SearchModal/sorting.ts index bb45f8668fd..48f34e670ef 100644 --- a/src/components/SearchModal/sorting.ts +++ b/src/components/SearchModal/sorting.ts @@ -1,4 +1,4 @@ -import { Token, TokenAmount } from '@ubeswap/sdk' +import { Token, TokenAmount } from 'lib/token-utils' import { useMemo } from 'react' import { useAllTokenBalances } from '../../state/wallet/hooks' diff --git a/src/components/TransactionConfirmationModal/index.tsx b/src/components/TransactionConfirmationModal/index.tsx index f652d6ebe55..5ccdeca670f 100644 --- a/src/components/TransactionConfirmationModal/index.tsx +++ b/src/components/TransactionConfirmationModal/index.tsx @@ -1,4 +1,4 @@ -import { ChainId } from '@ubeswap/sdk' +import { ChainId } from 'lib/token-utils' import React, { useContext } from 'react' import { AlertTriangle, ArrowUpCircle } from 'react-feather' import { Text } from 'rebass' diff --git a/src/components/Visx/GradientTextBox.tsx b/src/components/Visx/GradientTextBox.tsx deleted file mode 100644 index 95a74130aa6..00000000000 --- a/src/components/Visx/GradientTextBox.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { CardNoise } from 'components/claim/styled' -import { darken } from 'polished' -import React from 'react' -// import { -// GradientDarkgreenGreen, -// GradientLightgreenGreen, -// GradientOrangeRed, -// GradientPinkBlue, -// GradientPinkRed, -// GradientPurpleOrange, -// GradientPurpleRed, -// GradientTealBlue, -// RadialGradient, -// LinearGradient, -// } from '@visx/gradient' -// import { Bar } from '@visx/shape' -import styled from 'styled-components' - -import { TYPE } from '../../theme' -import { RowBetween } from '../Row' - -const defaultMargin = { - top: 0, - left: 0, - right: 0, - bottom: 0, -} - -const colors = ['#2172e5', '#EC7391', '#BF4B96', '#00BEBF', '#6C4871', '#E2AD57'] - -const InfoBox = styled.div<{ bgEnd: string }>` - width: 25%; - height: 4rem; - display: flex; - flex-direction: row; - margin: 0.5rem; - padding: 1rem; - border-radius: 0.5rem; - overflow: hidden; - position: relative; - background: radial-gradient( - 174.47% 188.91% at 1.84% 0%, - ${({ theme }) => theme.primary1} 0%, - ${({ bgEnd }) => darken(0.3, bgEnd)} 100% - ), - #edeef2; - ${({ theme }) => theme.mediaWidth.upToMedium` - width: 45%; - `} - ${({ theme }) => theme.mediaWidth.upToSmall` - width: 95%; - `} -` - -export type GradientTextProps = { - id: string - label: string - value: string - i?: number - margin?: typeof defaultMargin -} - -export default function GradientTextBox({ label, value, id, i = 0 }: GradientTextProps) { - const bgEnd = colors[i % colors.length] - return ( - <> - - - - {label} - {value} - - - - ) -} diff --git a/src/components/Visx/PieChart.tsx b/src/components/Visx/PieChart.tsx deleted file mode 100644 index 205ca709e47..00000000000 --- a/src/components/Visx/PieChart.tsx +++ /dev/null @@ -1,210 +0,0 @@ -import { GradientDarkgreenGreen } from '@visx/gradient' -import { Group } from '@visx/group' -import { scaleOrdinal } from '@visx/scale' -import Pie, { PieArcDatum, ProvidedProps } from '@visx/shape/lib/shapes/Pie' -import React, { useState } from 'react' -import { animated, interpolate, useTransition } from 'react-spring' -import styled from 'styled-components' - -export interface PieSection { - label: string - value: number -} - -const SVG = styled.svg` - width: 100%; - height: 50rem; -` - -// color scales -const defaultMargin = { top: 20, right: 20, bottom: 20, left: 20 } - -export type PieProps = { - width: number - height: number - margin?: typeof defaultMargin - animate?: boolean - innerChartData: PieSection[] - outerChartData: PieSection[] -} - -export default function DoublePieChart({ - width, - height, - innerChartData, - outerChartData, - margin = defaultMargin, - animate = true, -}: PieProps) { - const outerLabels = outerChartData.map(({ label }) => label) - const innerLabels = innerChartData.map(({ label }) => label) - const getOuterColor = scaleOrdinal({ - domain: outerLabels, - range: [ - 'rgba(255,255,255,0.7)', - 'rgba(255,255,255,0.6)', - 'rgba(255,255,255,0.5)', - 'rgba(255,255,255,0.4)', - 'rgba(255,255,255,0.3)', - 'rgba(255,255,255,0.2)', - 'rgba(255,255,255,0.1)', - ], - }) - const getInnerColor = scaleOrdinal({ - domain: innerLabels, - range: [ - 'rgba(93,30,91,1)', - 'rgba(93,30,91,0.9)', - 'rgba(93,30,91,0.8)', - 'rgba(93,30,91,0.7)', - 'rgba(93,30,91,0.6)', - 'rgba(93,30,91,0.5)', - 'rgba(93,30,91,0.4)', - 'rgba(93,30,91,0.3)', - 'rgba(93,30,91,0.2)', - 'rgba(93,30,91,0.1)', - ], - }) - - const [selectedOuter, setSelectedOuter] = useState(null) - const [selectedInner, setSelectedInner] = useState(null) - - if (width < 10) return null - - const innerWidth = width - margin.left - margin.right - const innerHeight = height - margin.top - margin.bottom - const radius = Math.min(innerWidth, innerHeight) / 2 - const centerY = innerHeight / 2 - const centerX = innerWidth / 2 - const donutThickness = 50 - - return ( - - - - - label === selectedOuter) : outerChartData} - pieValue={(e: PieSection) => e.value} - outerRadius={radius} - innerRadius={radius - donutThickness} - cornerRadius={3} - padAngle={0.005} - > - {(pie) => ( - - {...pie} - animate={animate} - getKey={(arc) => arc.data.label} - onClickDatum={({ data: { label } }) => - animate && setSelectedOuter(selectedOuter && selectedOuter === label ? null : label) - } - getColor={(arc) => getOuterColor(arc.data.label)} - /> - )} - - label === selectedInner) : innerChartData} - pieValue={(e: PieSection) => e.value} - pieSortValues={() => -1} - outerRadius={radius - donutThickness * 1.3} - > - {(pie) => ( - - {...pie} - animate={animate} - getKey={({ data: { label } }) => label} - onClickDatum={({ data: { label } }) => - animate && setSelectedInner(selectedInner && selectedInner === label ? null : label) - } - getColor={({ data: { label } }) => getInnerColor(label)} - /> - )} - - - {animate && ( - - Click segments to update - - )} - - ) -} - -// react-spring transition definitions -type AnimatedStyles = { startAngle: number; endAngle: number; opacity: number } - -const fromLeaveTransition = ({ endAngle }: PieArcDatum) => ({ - // enter from 360° if end angle is > 180° - startAngle: endAngle > Math.PI ? 2 * Math.PI : 0, - endAngle: endAngle > Math.PI ? 2 * Math.PI : 0, - opacity: 0, -}) -const enterUpdateTransition = ({ startAngle, endAngle }: PieArcDatum) => ({ - startAngle, - endAngle, - opacity: 1, -}) - -type AnimatedPieProps = ProvidedProps & { - animate?: boolean - getKey: (d: PieArcDatum) => string - getColor: (d: PieArcDatum) => string - onClickDatum: (d: PieArcDatum) => void - delay?: number -} - -function AnimatedPie({ animate, arcs, path, getKey, getColor, onClickDatum }: AnimatedPieProps) { - const transitions = useTransition, AnimatedStyles>(arcs, { - from: animate ? fromLeaveTransition : enterUpdateTransition, - enter: enterUpdateTransition, - update: enterUpdateTransition, - leave: animate ? fromLeaveTransition : enterUpdateTransition, - keys: getKey, - }) - return transitions((props, arc, { key }) => { - const [centroidX, centroidY] = path.centroid(arc) - const hasSpaceForLabel = arc.endAngle - arc.startAngle >= 0.1 - - return ( - - - path({ - ...arc, - startAngle, - endAngle, - }) - )} - fill={getColor(arc)} - onClick={() => onClickDatum(arc)} - onTouchStart={() => onClickDatum(arc)} - /> - {hasSpaceForLabel && ( - - - {getKey(arc)} - - - )} - - ) - }) -} diff --git a/src/components/VolumeChart/index.tsx b/src/components/VolumeChart/index.tsx index 67e35b291cc..ba7e3c3fba1 100644 --- a/src/components/VolumeChart/index.tsx +++ b/src/components/VolumeChart/index.tsx @@ -14,7 +14,6 @@ import { XYPlot, YAxis, } from 'react-vis' -import { useIsDarkMode } from 'state/user/hooks' import styled from 'styled-components' import { TYPE } from 'theme' @@ -48,7 +47,6 @@ export default function VolumeChart({ data, labels, width, xLabelFormat }: Volum const [area, setArea] = useState() const [hovered, setHovered] = useState() const [hoverValue, setHoverValue] = useState() - const isDarkMode = useIsDarkMode() const theme = useTheme() return ( @@ -66,8 +64,8 @@ export default function VolumeChart({ data, labels, width, xLabelFormat }: Volum {s} ))} orientation="horizontal" - onItemMouseEnter={(item, index, event) => setHovered(index)} - onItemMouseLeave={(item, index, event) => setHovered(undefined)} + onItemMouseEnter={(_, index) => setHovered(index)} + onItemMouseLeave={() => setHovered(undefined)} /> setHoverValue({ ...point, label: labels[i] })} - // onValueMouseOut={() => setHoverValue(undefined)} + onNearestXY={(point) => setHoverValue({ ...point, label: labels[i] })} /> ))} setArea(newArea)} onDrag={(newArea) => { - newArea && + !!area && + !!newArea && + area.top && + area.bottom && + newArea.top && + newArea.bottom && + area.right && + area.left && + newArea.right && + newArea.left && setArea({ bottom: area.bottom + (newArea.top - newArea.bottom), left: area.left - (newArea.right - newArea.left), diff --git a/src/components/claim/ClaimCard.tsx b/src/components/claim/ClaimCard.tsx index 8b5f714a18c..43f51351ef3 100644 --- a/src/components/claim/ClaimCard.tsx +++ b/src/components/claim/ClaimCard.tsx @@ -1,10 +1,9 @@ import { TransactionResponse } from '@ethersproject/providers' -import { VestingAddresses } from 'constants/StablePools' +import { VestingAddresses } from 'constants/tokens' import { darken } from 'polished' import React from 'react' import { VestType } from 'state/claim/reducer' import styled from 'styled-components' -import { humanFriendlyWei } from 'utils/eth' import { CHAIN } from '../../constants' import { useVestingContract } from '../../hooks/useContract' @@ -109,11 +108,11 @@ export const ClaimCard: React.FC = ({ info, type }: Props) => { const addTransaction = useTransactionAdder() async function onClaim() { - if (claimContract && unclaimedAmount) { + if (claimContract) { await claimContract?.['claim()']() .then((response: TransactionResponse) => { addTransaction(response, { - summary: `Claim ${humanFriendlyWei(unclaimedAmount.toString())} MOBI`, + summary: `Claim ${unclaimedAmount.toSignificant(3)} MOBI`, }) // setHash(response.hash) }) @@ -165,21 +164,21 @@ export const ClaimCard: React.FC = ({ info, type }: Props) => { Total allocated amount - {allocatedAmount ? humanFriendlyWei(allocatedAmount.toString()) : '--'} + {allocatedAmount.toSignificant(3)}
Claimed amount - {claimedAmount ? humanFriendlyWei(claimedAmount.toString()) : '--'} + {claimedAmount.toSignificant(3)}
Unclaimed vested amount - {unclaimedAmount ? humanFriendlyWei(unclaimedAmount.toString()) : '--'} + {unclaimedAmount.toSignificant(3)}
diff --git a/src/components/earn/ClaimExternalRewardsModal.tsx b/src/components/earn/ClaimExternalRewardsModal.tsx index edce65f6ef9..caf5ff74f1c 100644 --- a/src/components/earn/ClaimExternalRewardsModal.tsx +++ b/src/components/earn/ClaimExternalRewardsModal.tsx @@ -1,13 +1,10 @@ import { TransactionResponse } from '@ethersproject/providers' -import { TokenAmount } from '@ubeswap/sdk' -import { useMobi } from 'hooks/Tokens' -import React, { useEffect, useState } from 'react' -import { useBlockNumber } from 'state/application/hooks' -import { StablePoolInfo, useExternalRewards } from 'state/stablePools/hooks' +import { TokenAmount } from 'lib/token-utils' +import React, { useCallback, useState } from 'react' import styled from 'styled-components' import { useWeb3Context } from '../../hooks' -import { useLiquidityGaugeContract, useMobiMinterContract } from '../../hooks/useContract' +import { useLiquidityGaugeContract } from '../../hooks/useContract' import { useTransactionAdder } from '../../state/transactions/hooks' import { CloseIcon, TYPE } from '../../theme' import { ButtonError } from '../Button' @@ -24,13 +21,12 @@ const ContentWrapper = styled(AutoColumn)` interface StakingModalProps { isOpen: boolean onDismiss: () => void - stakingInfo: StablePoolInfo + externalRewards: TokenAmount[] | undefined + gaugeAddress: string } -export default function ExternalRewardsModal({ isOpen, onDismiss, stakingInfo }: StakingModalProps) { +export default function ExternalRewardsModal({ isOpen, onDismiss, externalRewards, gaugeAddress }: StakingModalProps) { const { address, connected } = useWeb3Context() - const mobi = useMobi() - const externalRewards = useExternalRewards({ address: stakingInfo.poolAddress ?? '' }) // monitor call to help UI loading state const addTransaction = useTransactionAdder() const [hash, setHash] = useState() @@ -42,23 +38,10 @@ export default function ExternalRewardsModal({ isOpen, onDismiss, stakingInfo }: onDismiss() } - const stakingContract = useLiquidityGaugeContract(stakingInfo.gaugeAddress) - const minter = useMobiMinterContract() + const stakingContract = useLiquidityGaugeContract(gaugeAddress) - const [pendingMobi, setEarnedMobi] = useState() - - const blockNumber = useBlockNumber() - - useEffect(() => { - const updateMobi = async () => { - const bigInt = await stakingContract?.claimable_tokens(address) - setEarnedMobi(new TokenAmount(mobi, bigInt.toString())) - } - connected && updateMobi() - }, [stakingContract, setEarnedMobi, connected, address, mobi]) - - async function onClaimReward() { - if (stakingContract && stakingInfo?.stakedAmount) { + const onClaimReward = useCallback(async () => { + if (stakingContract) { setAttempting(true) await stakingContract['claim_rewards(address)'](address, { gasLimit: 1000000 }) .then((response: TransactionResponse) => { @@ -72,15 +55,12 @@ export default function ExternalRewardsModal({ isOpen, onDismiss, stakingInfo }: console.log(error) }) } - } + }, [addTransaction, address, stakingContract]) let error: string | undefined if (!connected) { error = 'Connect Wallet' } - if (!stakingInfo?.stakedAmount) { - error = error ?? 'Enter an amount' - } return ( @@ -90,30 +70,20 @@ export default function ExternalRewardsModal({ isOpen, onDismiss, stakingInfo }: Claim - {pendingMobi && ( - - {externalRewards && - externalRewards.map((reward) => ( - - {reward.toSignificant(6)} {reward.token.symbol} - - ))} - {/* {stakingInfo?.dualRewards && ( - - {stakingInfo?.earnedAmount?.toSignificant(6)} {stakingInfo?.rewardToken?.symbol} + + + {externalRewards && + externalRewards.map((reward) => ( + + {reward.toSignificant(6)} {reward.token.symbol} - )} */} - Unclaimed rewards - - )} + ))} + Unclaimed rewards + When you claim without withdrawing your liquidity remains in the mining pool. - + {error ?? 'Claim'} diff --git a/src/components/earn/ClaimRewardModal.tsx b/src/components/earn/ClaimRewardModal.tsx index 255cea03c84..df65fcbee81 100644 --- a/src/components/earn/ClaimRewardModal.tsx +++ b/src/components/earn/ClaimRewardModal.tsx @@ -1,13 +1,10 @@ import { TransactionResponse } from '@ethersproject/providers' -import { TokenAmount } from '@ubeswap/sdk' -import { useMobi } from 'hooks/Tokens' -import React, { useEffect, useState } from 'react' -import { useBlockNumber } from 'state/application/hooks' -import { StablePoolInfo } from 'state/stablePools/hooks' +import React, { useCallback, useState } from 'react' +import { UserGaugeInfo } from 'state/gauges/hooks' import styled from 'styled-components' import { useWeb3Context } from '../../hooks' -import { useLiquidityGaugeContract, useMobiMinterContract } from '../../hooks/useContract' +import { useMobiMinterContract } from '../../hooks/useContract' import { useTransactionAdder } from '../../state/transactions/hooks' import { CloseIcon, TYPE } from '../../theme' import { ButtonError } from '../Button' @@ -24,12 +21,12 @@ const ContentWrapper = styled(AutoColumn)` interface StakingModalProps { isOpen: boolean onDismiss: () => void - stakingInfo: StablePoolInfo + userGaugeInfo: UserGaugeInfo + gaugeAddress: string } -export default function ClaimRewardModal({ isOpen, onDismiss, stakingInfo }: StakingModalProps) { - const { address, connected } = useWeb3Context() - const mobi = useMobi() +export default function ClaimRewardModal({ isOpen, onDismiss, userGaugeInfo, gaugeAddress }: StakingModalProps) { + const { connected } = useWeb3Context() // monitor call to help UI loading state const addTransaction = useTransactionAdder() @@ -42,26 +39,13 @@ export default function ClaimRewardModal({ isOpen, onDismiss, stakingInfo }: Sta onDismiss() } - const stakingContract = useLiquidityGaugeContract(stakingInfo.gaugeAddress) const minter = useMobiMinterContract() - const [pendingMobi, setEarnedMobi] = useState() - - const blockNumber = useBlockNumber() - - useEffect(() => { - const updateMobi = async () => { - const bigInt = await stakingContract?.claimable_tokens(address) - setEarnedMobi(new TokenAmount(mobi, bigInt.toString())) - } - connected && updateMobi() - }, [stakingContract, setEarnedMobi, address, mobi, connected]) - - async function onClaimReward() { - if (stakingContract && stakingInfo?.stakedAmount) { + const onClaimReward = useCallback(async () => { + if (minter) { setAttempting(true) await minter - .mint(stakingInfo.gaugeAddress, { gasLimit: 350000 }) + .mint(gaugeAddress, { gasLimit: 350000 }) .then((response: TransactionResponse) => { addTransaction(response, { summary: `Claim accumulated MOBI rewards`, @@ -73,15 +57,12 @@ export default function ClaimRewardModal({ isOpen, onDismiss, stakingInfo }: Sta console.log(error) }) } - } + }, [addTransaction, gaugeAddress, minter]) let error: string | undefined if (!connected) { error = 'Connect Wallet' } - if (!stakingInfo?.stakedAmount) { - error = error ?? 'Enter an amount' - } return ( @@ -91,23 +72,16 @@ export default function ClaimRewardModal({ isOpen, onDismiss, stakingInfo }: Sta Claim - {pendingMobi && ( - - - {pendingMobi.toSignificant(6)} MOBI - - {/* {stakingInfo?.dualRewards && ( - - {stakingInfo?.earnedAmount?.toSignificant(6)} {stakingInfo?.rewardToken?.symbol} - - )} */} - Unclaimed rewards - - )} + + + {userGaugeInfo.claimableMobi.toSignificant(6)} MOBI + + Unclaimed rewards + When you claim without withdrawing your liquidity remains in the mining pool. - + {error ?? 'Claim'} @@ -115,7 +89,7 @@ export default function ClaimRewardModal({ isOpen, onDismiss, stakingInfo }: Sta {attempting && !hash && ( - Claiming {pendingMobi.toSignificant(6)} MOBI + Claiming {userGaugeInfo.claimableMobi.toSignificant(6)} MOBI )} diff --git a/src/components/earn/DepositModal.tsx b/src/components/earn/DepositModal.tsx index 155d5f55544..5db0b134262 100644 --- a/src/components/earn/DepositModal.tsx +++ b/src/components/earn/DepositModal.tsx @@ -1,16 +1,18 @@ import { TransactionResponse } from '@ethersproject/providers' -import { JSBI, TokenAmount } from '@ubeswap/sdk' import CurrencyLogo from 'components/CurrencyLogo' -import React, { useState } from 'react' +import { useWarning } from 'hooks/useWarning' +import { calculateEstimatedMintAmount, calculateVirtualPrice } from 'lib/calculator' +import { Fraction, TokenAmount } from 'lib/token-utils' +import { Meta } from 'pages/Pool' +import React, { useMemo, useState } from 'react' import { tryParseAmount } from 'state/swap/hooks' import styled from 'styled-components' -import { weiScale } from '../../constants' +import { BIPS_BASE, weiScale } from '../../constants' import { useWeb3Context } from '../../hooks' import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback' import { useStableSwapContract } from '../../hooks/useContract' import useTransactionDeadline from '../../hooks/useTransactionDeadline' -import { StablePoolInfo, useExpectedLpTokens, useWarning } from '../../state/stablePools/hooks' import { useTransactionAdder } from '../../state/transactions/hooks' import { useTokenBalance } from '../../state/wallet/hooks' import { CloseIcon, ExternalLink, TYPE } from '../../theme' @@ -36,62 +38,59 @@ const ApprovalButton = styled(ButtonPrimary)` interface DepositModalProps { isOpen: boolean onDismiss: () => void - poolInfo: StablePoolInfo + meta: Meta } -export default function DepositModal({ isOpen, onDismiss, poolInfo }: DepositModalProps) { - const { address, connected } = useWeb3Context() +export default function DepositModal({ isOpen, onDismiss, meta }: DepositModalProps) { + const { connected } = useWeb3Context() + const warning = useWarning(meta.display.warningType) // monitor call to help UI loading state const addTransaction = useTransactionAdder() - const { tokens, peggedTo, pegComesAfter, totalDeposited } = poolInfo - const warning = useWarning(poolInfo.poolAddress ?? undefined) const [hash, setHash] = useState() const [attempting, setAttempting] = useState(false) const [approving, setApproving] = useState(false) - const [input, setInput] = useState<(string | undefined)[]>(new Array(tokens.length).fill(undefined)) + const [input, setInput] = useState(new Array(meta.display.pool.tokens.length).fill('')) const [warningAcknowledged, setWarningAcknowledged] = useState(!warning) - const isFirstDeposit = totalDeposited.equalTo('0') + + const isFirstDeposit = meta.exchangeInfo.lpTotalSupply.equalTo(0) const forceEqualDeposit = isFirstDeposit || warning?.modification === 'require-equal-deposit' const [useEqualAmount, setUseEqualAmount] = useState(forceEqualDeposit) const deadline = useTransactionDeadline() - const sumAmount = tokens - .map((t, i) => - JSBI.multiply( - tryParseAmount(input[i], t)?.raw ?? JSBI.BigInt(0), - JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18 - t.decimals)) - ) + const inputTokens = useMemo(() => { + return input.map( + (el, i) => tryParseAmount(el, meta.display.pool.tokens[i]) ?? new TokenAmount(meta.display.pool.tokens[i], 0) ) - .reduce((acc, cur) => JSBI.add(acc, cur), JSBI.BigInt(0)) + }, [input, meta.display.pool.tokens]) - const [expectedLPTokens, selectedAmounts] = useExpectedLpTokens(poolInfo, tokens, input) - const valueOfLP = new TokenAmount( - poolInfo.lpToken, - JSBI.divide( - JSBI.multiply(expectedLPTokens.raw, poolInfo.virtualPrice), - JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('18')) - ) - ) + const sumAmount = inputTokens.reduce((acc, cur) => acc.add(cur), new Fraction(0)) + + const expectedAmounts = calculateEstimatedMintAmount(meta.exchangeInfo, inputTokens[0].raw, inputTokens[1].raw) - const diff = JSBI.greaterThan(valueOfLP.raw, sumAmount) - ? JSBI.subtract(valueOfLP.raw, sumAmount) - : JSBI.subtract(sumAmount, valueOfLP.raw) + const virtualPrice = calculateVirtualPrice(meta.exchangeInfo) + const adjustedExpectedAmount = expectedAmounts.mintAmount.multiply(virtualPrice ?? 1) - const perDiff = JSBI.equal(sumAmount, JSBI.BigInt(0)) - ? JSBI.BigInt(0) - : JSBI.divide(JSBI.multiply(diff, weiScale), sumAmount) + const diff = sumAmount.greaterThan(adjustedExpectedAmount) + ? sumAmount.subtract(adjustedExpectedAmount) + : adjustedExpectedAmount.subtract(sumAmount) - const decimalPlacesForLP = expectedLPTokens?.greaterThan('1') ? 2 : expectedLPTokens?.greaterThan('0') ? 10 : 2 + const perDiff = sumAmount.equalTo(0) ? new Fraction(0) : diff.divide(sumAmount).multiply(BIPS_BASE) - const withSlippage = JSBI.subtract(expectedLPTokens.raw, JSBI.divide(expectedLPTokens.raw, JSBI.BigInt('10'))) + const decimalPlacesForLP = expectedAmounts.mintAmount.greaterThan(1) + ? 2 + : expectedAmounts.mintAmount.greaterThan(0) + ? 10 + : 2 + + const expectAmountWithSlippage = expectedAmounts.mintAmount.multiply(98).divide(100).multiply(weiScale) const approvals = [ - useApproveCallback(selectedAmounts[0], poolInfo.poolAddress), - useApproveCallback(selectedAmounts[1], poolInfo.poolAddress), + useApproveCallback(inputTokens[0], meta.display.pool.address), + useApproveCallback(inputTokens[1], meta.display.pool.address), ] const toApprove = approvals - .map(([approvalState], i) => { - if (approvalState !== ApprovalState.APPROVED) return i - else return null + .map(([approvalState]) => { + if (approvalState !== ApprovalState.APPROVED) return true + else return false }) .filter((x) => x !== null) function wrappedOndismiss() { @@ -100,17 +99,18 @@ export default function DepositModal({ isOpen, onDismiss, poolInfo }: DepositMod onDismiss() } - const stakingContract = useStableSwapContract(poolInfo.poolAddress) + const stakingContract = useStableSwapContract(meta.display.pool.address) async function onDeposit() { - const allValid = selectedAmounts.reduce((accum, cur) => accum && !!cur && !!cur.raw, true) - if (stakingContract && poolInfo?.stakedAmount && allValid) { + if (stakingContract && deadline) { setAttempting(true) - const tokenAmounts = selectedAmounts.map((amount) => BigInt(amount.raw.toString())) + console.log(1) + const tokenAmounts = inputTokens.map((el) => el.raw.toString()) + console.log(2, tokenAmounts) await stakingContract - .addLiquidity(tokenAmounts, withSlippage.toString(), deadline, { gasLimit: 10000000 }) + .addLiquidity(tokenAmounts, expectAmountWithSlippage.quotient.toString(), deadline, { gasLimit: 10000000 }) .then((response: TransactionResponse) => { addTransaction(response, { - summary: `Deposit Liquidity into ${poolInfo.name}`, + summary: `Deposit Liquidity into ${meta.display.name}`, }) setHash(response.hash) }) @@ -125,8 +125,10 @@ export default function DepositModal({ isOpen, onDismiss, poolInfo }: DepositMod if (!connected) { error = 'Connect Wallet' } - if (!poolInfo?.stakedAmount) { - error = error ?? 'Enter an amount' + + const display = (str: string): string => { + const peg = meta.display.peg + return (peg.position === 'before' ? peg.symbol : '').concat(str).concat(peg.position === 'after' ? peg.symbol : '') } return ( @@ -146,7 +148,7 @@ export default function DepositModal({ isOpen, onDismiss, poolInfo }: DepositMod ) : ( <> - Deposit to {poolInfo.name} + Deposit to {meta.display.name} {forceEqualDeposit && ( @@ -169,21 +171,20 @@ export default function DepositModal({ isOpen, onDismiss, poolInfo }: DepositMod toggle={() => !forceEqualDeposit && setUseEqualAmount(!useEqualAmount)} /> - {poolInfo.tokens.map((token, i) => ( -
+ {meta.display.pool.tokens.map((token, i) => ( +
{ if (useEqualAmount) { - setInput(new Array(tokens.length).fill(val)) + setInput(new Array(meta.display.pool.tokens.length).fill(val)) } else { setInput([...input.slice(0, i), val, ...input.slice(i + 1)]) } }} - // setUsingInsufficientFunds={setInsufficientFunds} /> - {i !== selectedAmounts.length - 1 && ( + {i !== inputTokens.length - 1 && ( + @@ -191,41 +192,44 @@ export default function DepositModal({ isOpen, onDismiss, poolInfo }: DepositMod
))} - Expected Lp Tokens Received: {expectedLPTokens.toFixed(decimalPlacesForLP)} + Expected Lp Tokens Received: {expectedAmounts.mintAmount.toFixed(decimalPlacesForLP)} {!isFirstDeposit && ( - Equivalent to: {pegComesAfter ? '' : peggedTo} - {valueOfLP.toFixed(4)} {pegComesAfter ? poolInfo.peggedTo : ''} + Equivalent to: {display(adjustedExpectedAmount.toFixed(4))} )} - {toApprove.length > 0 && expectedLPTokens.greaterThan('0') && ( + {toApprove.includes(true) && expectedAmounts.mintAmount.greaterThan('0') && (
- {toApprove.map((i) => ( - { - setApproving(true) - await approvals[i][1]() - await new Promise((resolve) => setTimeout(resolve, 20000)) - setApproving(false) - }} - > - Approve {tokens[i].symbol} - - ))} + {toApprove.map( + (el, i) => + el && ( + { + setApproving(true) + await approvals[i][1]() + // TODO: see if we can remove this time + await new Promise((resolve) => setTimeout(resolve, 20000)) + setApproving(false) + }} + > + Approve {meta.display.pool.tokens[i].symbol} + + ) + )}
)} - {toApprove.length === 0 && ( - + {!toApprove.includes(true) && expectedAmounts.mintAmount.greaterThan('0') && ( + {error ?? 'Deposit'} )} @@ -237,7 +241,7 @@ export default function DepositModal({ isOpen, onDismiss, poolInfo }: DepositMod Depositing - Claiming {expectedLPTokens.toSignificant(4)} LP Tokens + Claiming {expectedAmounts.mintAmount.toSignificant(4)} LP Tokens )} @@ -257,18 +261,13 @@ type CurrencyRowProps = { tokenAmount: TokenAmount setInput: (amount: string) => void input: string - setUsingInsufficientFunds: (isInsufficient: boolean) => void } -const InputRowLeft = styled.div`` - -const TokenInfo = styled.div`` - -const InputRow = styled.div<{ selected: boolean }>` +const InputRow = styled.div` ${({ theme }) => theme.flexRowNoWrap}; align-items: center; justify-content: space-between; - padding: ${({ selected }) => (selected ? '0.75rem 0.5rem 0.75rem 1rem' : '0.75rem 0.75rem 0.75rem 1rem')}; + padding: 0.75rem 0.75rem 0.75rem 1rem; ` const InputDiv = styled.div` @@ -282,22 +281,6 @@ const Aligner = styled.span` justify-content: space-between; ` -const InputPanel = styled.div<{ hideInput?: boolean }>` - ${({ theme }) => theme.flexColumnNoWrap} - position: relative; - border-radius: ${({ hideInput }) => (hideInput ? '8px' : '20px')}; - background-color: ${({ theme }) => theme.bg1}; - z-index: 1; - width: 100%; -` - -const Container = styled.div<{ hideInput: boolean }>` - border-radius: ${({ hideInput }) => (hideInput ? '8px' : '20px')}; - border: 1px solid ${({ theme }) => theme.bg2}; - background-color: ${({ theme }) => theme.bg1}; - padding: 0.5rem; -` - const StyledTokenName = styled.span<{ active?: boolean }>` ${({ active }) => (active ? ' margin: 0 0.25rem 0 0.75rem;' : ' margin: 0 0.25rem 0 0.25rem;')} font-size: ${({ active }) => (active ? '20px' : '16px')}; @@ -307,15 +290,10 @@ const BalanceText = styled(TYPE.subHeader)` cursor: pointer; ` -const CurrencyRow = ({ tokenAmount, setInput, input, setUsingInsufficientFunds }: CurrencyRowProps) => { +const CurrencyRow = ({ tokenAmount, setInput, input }: CurrencyRowProps) => { const { address, connected } = useWeb3Context() - const currency = tokenAmount.currency + const currency = tokenAmount.token const tokenBalance = useTokenBalance(connected ? address : undefined, currency ?? undefined) - const TEN = JSBI.BigInt('10') - const ZERO_TOK = new TokenAmount(currency, JSBI.BigInt('0')) - - const scaledDown = (num: JSBI) => JSBI.divide(num, JSBI.exponentiate(TEN, JSBI.BigInt(currency.decimals))) - const scaleUp = (num: JSBI) => JSBI.multiply(num, JSBI.exponentiate(TEN, JSBI.BigInt(currency.decimals))) const decimalPlacesForBalance = tokenBalance?.greaterThan( '1' //JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt(tokenBalance.token.decimals - 2)).toString() @@ -341,7 +319,6 @@ const CurrencyRow = ({ tokenAmount, setInput, input, setUsingInsufficientFunds }
{ @@ -366,11 +343,3 @@ const CurrencyRow = ({ tokenAmount, setInput, input, setUsingInsufficientFunds }
) } - -const insertDecimal = (tokenAmount: TokenAmount) => { - const { token } = tokenAmount - const amount = tokenAmount.divide( - new TokenAmount(token, JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt(token.decimals))) - ) - return amount.toFixed(2) -} diff --git a/src/components/earn/StablePoolCard.tsx b/src/components/earn/StablePoolCard.tsx index 719adb2d103..1ca2d3fc1c2 100644 --- a/src/components/earn/StablePoolCard.tsx +++ b/src/components/earn/StablePoolCard.tsx @@ -1,22 +1,23 @@ -import { cUSD, Fraction, JSBI, Percent, Price, TokenAmount } from '@ubeswap/sdk' -import Loader from 'components/Loader' import QuestionHelper from 'components/QuestionHelper' -import { ChainLogo, Coins } from 'constants/StablePools' +import { ChainLogo, Coins } from 'constants/pools' import { useWeb3Context } from 'hooks' import { useMobi } from 'hooks/Tokens' +import { useValueOfExternalRewards } from 'hooks/useStablePools' +import { calculateVirtualPrice } from 'lib/calculator' +import { Fraction, TokenAmount } from 'lib/token-utils' +import { Meta } from 'pages/Pool' import { darken } from 'polished' -import React, { useState } from 'react' +import React, { useCallback, useMemo, useState } from 'react' import { isMobile } from 'react-device-detect' import { useHistory } from 'react-router' import { NavLink } from 'react-router-dom' +import { useMobiPrice, usePegPrice } from 'state/application/hooks' +import { StakingInfo } from 'state/staking/hooks' import styled from 'styled-components' -import { getDepositValues } from 'utils/stableSwaps' -import { getCUSDPrices, useCUSDPrice } from 'utils/useCUSDPrice' +import { calcRates } from 'utils/calcRate' -import { BIG_INT_SECONDS_IN_WEEK, BIG_INT_SECONDS_IN_YEAR, CHAIN } from '../../constants' -import { useColor, usePoolColor } from '../../hooks/useColor' -import { useTokenPrices } from '../../state/application/hooks' -import { StablePoolInfo } from '../../state/stablePools/hooks' +import { BIG_INT_SECONDS_IN_WEEK, BIG_INT_SECONDS_IN_YEAR } from '../../constants' +import { usePoolColor } from '../../hooks/useColor' import { theme, TYPE } from '../../theme' import { ButtonPrimary } from '../Button' import { AutoColumn } from '../Column' @@ -138,220 +139,149 @@ const StyledNavLink = styled(NavLink)<{ color: string }>` textDecoration: underline, ` -export function calcApy(rewardPerYear: Fraction, totalStakedAmount: Fraction) { - const apyFraction = rewardPerYear - .multiply(JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('18'))) - .divide(totalStakedAmount) - - const apy = apyFraction - ? new Percent( - apyFraction.numerator, - JSBI.multiply(apyFraction.denominator, JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('18'))) - ) - : undefined - - const dpy = apy - ? new Percent(Math.floor(parseFloat(apy.divide('365').toFixed(10)) * 1_000_000).toFixed(0), '1000000') - : undefined - return [apyFraction, apy, dpy] -} - interface Props { - poolInfo: StablePoolInfo + meta: Meta + stakingInfo: StakingInfo } -export const StablePoolCard: React.FC = ({ poolInfo }: Props) => { +export const StablePoolCard: React.FC = ({ meta, stakingInfo }: Props) => { const { connect, connected } = useWeb3Context() - const { - tokens, - peggedTo, - balances, - totalDeposited, - stakedAmount, - workingSupply, - pegComesAfter, - feesGenerated, - mobiRate, - displayDecimals, - totalStakedAmount: totalStakedLPs, - coin, - } = poolInfo + const history = useHistory() const [openDeposit, setOpenDeposit] = useState(false) const [openWithdraw, setOpenWithdraw] = useState(false) const [openManage, setOpenManage] = useState(false) - const tokenPrices = getCUSDPrices(useTokenPrices()) - const history = useHistory() const mobi = useMobi() - const priceOfMobi = useCUSDPrice(mobi) ?? new Price(mobi, cUSD[CHAIN], '100', '1') - const userLP = poolInfo.amountDeposited - const { totalValueDeposited, valueOfDeposited } = getDepositValues(poolInfo, workingSupply) - const coinPrice = tokens.reduce( - (accum: Fraction | undefined, { address }) => accum ?? tokenPrices[address.toLowerCase()], - undefined - ) + const mobiPrice = useMobiPrice() - const totalStakedAmount = totalValueDeposited - ? totalValueDeposited.multiply(new Fraction(coinPrice?.numerator ?? '1', coinPrice?.denominator ?? '1')) - : new Fraction(JSBI.BigInt(0)) - const totalMobiRate = new TokenAmount(mobi, mobiRate ?? JSBI.BigInt('0')) + const pegPrice = usePegPrice(meta.display.peg.priceQuery) - const rewardPerYear = priceOfMobi.raw.multiply(totalMobiRate.multiply(BIG_INT_SECONDS_IN_YEAR)) - let rewardPerYearExternal = new Fraction('0', '1') - for (let i = 0; i < 8; i++) { - const rate = poolInfo.externalRewardRates?.[i] ?? totalMobiRate - const priceOfToken = - tokenPrices[rate.token.address.toLowerCase()] ?? tokenPrices['0x00be915b9dcf56a3cbe739d9b9c202ca692409ec'] - if (poolInfo.externalRewardRates && i < poolInfo.externalRewardRates.length) { - rewardPerYearExternal = rewardPerYearExternal.add( - priceOfToken?.multiply(rate.multiply(BIG_INT_SECONDS_IN_YEAR)) ?? '0' - ) - } - } - const [apyFraction, apy, dpy] = - mobiRate && totalStakedAmount && !totalStakedAmount.equalTo(JSBI.BigInt(0)) - ? calcApy(rewardPerYear.add(rewardPerYearExternal), totalStakedAmount) - : [undefined, undefined, undefined] + const externalRewardValue = useValueOfExternalRewards(meta.display.gauge).multiply(BIG_INT_SECONDS_IN_YEAR) - const [boostedApyFraction, boostedApy, boostedDpy] = - mobiRate && totalStakedAmount && !totalStakedAmount.equalTo(JSBI.BigInt(0)) - ? calcApy( - rewardPerYear.multiply(new Fraction(JSBI.BigInt(5), JSBI.BigInt(2))).add(rewardPerYearExternal), - totalStakedAmount - ) - : [new Fraction('0', '1'), new Fraction('0', '1'), new Fraction('0', '1')] + const poolColor = usePoolColor(meta.display) - let weeklyAPY: React.ReactNode | undefined = <>🤯 - try { - weeklyAPY = apy - ? new Percent( - Math.floor(parseFloat(apy.divide('52').add('1').toFixed(10)) ** 52 * 1_000_000 - 1_000_000).toFixed(0), - '1000000' - ).toFixed(0, { groupSeparator: ',' }) - : undefined - } catch (e) { - console.error('Weekly apy overflow', e) - } - let userBalances: TokenAmount[] = [] - if (totalDeposited.greaterThan('0')) { - userBalances = balances.map((amount) => { - const fraction = new Fraction(userLP ? userLP.raw : JSBI.BigInt(0), totalDeposited.raw) - const ratio = fraction.multiply(amount.raw) - return new TokenAmount(amount.currency, JSBI.divide(ratio.numerator, ratio.denominator)) - }) - } - // get the color of the token - const backgroundColorStart = useColor(tokens[0]) - let backgroundColorEnd = useColor(tokens[tokens.length - 1]) - const poolColor = usePoolColor(poolInfo) - const backgroundGradient = null //generateGradient(tokens.slice()) + const virtualPrice = calculateVirtualPrice(meta.exchangeInfo) - if (!backgroundColorEnd || backgroundColorEnd === backgroundColorStart) backgroundColorEnd = '#212429' + const { totalDeposited, userDeposited, mobiRate, apr, dpr, apy, boostedApr } = useMemo(() => { + const totalDeposited = + virtualPrice?.multiply(meta.exchangeInfo.lpTotalSupply) ?? new TokenAmount(meta.display.pool.lpToken, 0) - // get the USD value of staked WETH - // const apyFraction = poolInfo.apr || undefined - // const apy = apyFraction ? new Percent(apyFraction.numerator, apyFraction.denominator) : undefined - const isStaking = valueOfDeposited.greaterThan(JSBI.BigInt('0')) || poolInfo.stakedAmount.greaterThan('0') + const userDeposited = new TokenAmount( + meta.display.pool.lpToken, + virtualPrice?.multiply(meta.lpBalance.asFraction.add(meta.userGauge?.balance ?? 0)).quotient ?? 0 + ) - const formatNumber = (num: string) => { - return num.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,') - } + const totalDepositedValue = pegPrice ? totalDeposited.multiply(pegPrice) : new Fraction(0) + + const mobiRate = new TokenAmount(mobi, meta.gauge?.weight.multiply(stakingInfo.mobiRate).quotient ?? 0) + const mobiRateValue = mobiPrice.multiply(mobiRate).multiply(BIG_INT_SECONDS_IN_YEAR) + + // TODO: investigate if this is the right method + const { apr, dpr, apy } = calcRates(mobiRateValue.add(externalRewardValue), totalDepositedValue) + const { apr: boostedApr } = calcRates(mobiRateValue.add(externalRewardValue), totalDepositedValue) - const totalDisplay = (amount: TokenAmount): string => { - if (coin === Coins.Bitcoin || coin === Coins.Ether) { - if (JSBI.lessThan(amount.raw, JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(19))) || openManage) { - return amount.toFixed(2) - } else return amount.toFixed(0) - } else { - if (JSBI.lessThan(amount.raw, JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(23))) || openManage) { - return formatNumber(amount.toFixed(0)) - } else { - const collapsed = JSBI.divide(amount.raw, JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(23))).toString() - return formatNumber(String((Number(collapsed) / 10).toFixed(1))).concat('M') - } + return { + totalDeposited, + userDeposited, + mobiRate, + apr, + dpr, + apy, + boostedApr, } - } + }, [ + externalRewardValue, + meta.display.pool.lpToken, + meta.exchangeInfo.lpTotalSupply, + meta.gauge?.weight, + meta.lpBalance.asFraction, + meta.userGauge?.balance, + mobi, + mobiPrice, + pegPrice, + stakingInfo.mobiRate, + virtualPrice, + ]) + + const balances = meta.exchangeInfo.reserves + const display = useCallback( + (str: string): string => { + const peg = meta.display.peg + return (peg.position === 'before' ? peg.symbol : '') + .concat(str) + .concat(peg.position === 'after' ? peg.symbol : '') + }, + [meta.display.peg] + ) + + const totalDisplay = useCallback( + (amount: TokenAmount | Fraction): string => { + const decimals = meta.display.peg.decimals + if (amount.lessThan(10 ** (2 - decimals))) return display(amount.toFixed(decimals + 1, { groupSeparator: ',' })) + else if (amount.lessThan(10 ** 6) || openManage) return display(amount.toFixed(decimals, { groupSeparator: ',' })) + else + return display( + amount + .divide(10 ** 6) + .toFixed(2, { groupSeparator: ',' }) + .concat('M') + ) + }, + [display, meta.display.peg.decimals, openManage] + ) return ( - setOpenManage(!openManage)} - > - {openDeposit && setOpenDeposit(false)} poolInfo={poolInfo} />} - {openWithdraw && ( - setOpenWithdraw(false)} poolInfo={poolInfo} /> - )} + setOpenManage(!openManage)}> + {openDeposit && setOpenDeposit(false)} meta={meta} />} + {openWithdraw && setOpenWithdraw(false)} meta={meta} />} - {poolInfo.name} + {meta.display.name} - + + + + + Yield/day: {dpr?.toSignificant(4)}%
+ APY (daily compounded): {apy.toSignificant(4)}% + + } + /> + + {apr.denominator.toString() !== '0' ? `${apr.toFixed(1, { groupSeparator: ',' })}%` : ' -'} APR +
- {apy ? ( - - - Yield/day: {dpy?.toSignificant(4)}%
- APY (weekly compounded): {weeklyAPY}% - - } - /> - - {apy.denominator.toString() !== '0' ? `${apy.toFixed(1, { groupSeparator: ',' })}%` : ' -'} APR - -
- ) : ( - - - APY Loading - - )}
- + - {tokens.map((t) => t.symbol).join(' / ')} + {meta.display.pool.tokens.map((t) => t.symbol).join(' / ')} - {poolInfo.meta && ( - - A meta pool pairs one token with the LP token of another pool to build on already-existing liquidity.{' '} -
-
- This meta pool builds off of {poolInfo.meta} - - } - /> - )}
- {apy && boostedApy ? ( - - - up to {apy.denominator.toString() !== '0' ? `${boostedApy.toFixed(1, { groupSeparator: ',' })}%` : ' -'}{' '} - w/ boost - - - ) : null} + + + + up to {apy.denominator.toString() !== '0' ? `${boostedApr.toFixed(1, { groupSeparator: ',' })}%` : ' -'} w/ + boost + +
@@ -364,17 +294,13 @@ export const StablePoolCard: React.FC = ({ poolInfo }: Props) => { text={balances .map( (balance) => - `${balance?.toFixed(displayDecimals, { groupSeparator: ',' })} ${balance.token.symbol}` + `${balance?.toFixed(meta.display.peg.decimals, { groupSeparator: ',' })} ${ + balance.token.symbol + }` ) .join(', ')} /> - - {totalValueDeposited - ? `${!pegComesAfter ? peggedTo : ''}${totalDisplay(totalValueDeposited)} ${ - pegComesAfter ? peggedTo : '' - }` - : '-'} - + {totalDisplay(totalDeposited)} @@ -382,10 +308,8 @@ export const StablePoolCard: React.FC = ({ poolInfo }: Props) => { Weekly volume - {poolInfo.weeklyVolume - ? `${!pegComesAfter ? peggedTo : ''}${totalDisplay(poolInfo.weeklyVolume)} ${ - pegComesAfter ? peggedTo : '' - }` + {meta.volume.volume + ? totalDisplay(new Fraction(Math.floor(meta.volume.volume?.week))) : 'Subgraph Syncing...'} @@ -397,10 +321,8 @@ export const StablePoolCard: React.FC = ({ poolInfo }: Props) => { Total volume - {poolInfo.totalVolume - ? `${!pegComesAfter ? peggedTo : ''}${totalDisplay(poolInfo.totalVolume)} ${ - pegComesAfter ? peggedTo : '' - }` + {meta.volume.volume + ? totalDisplay(new Fraction(Math.floor(meta.volume.volume?.total))) : 'Subgraph Syncing...'} @@ -410,30 +332,26 @@ export const StablePoolCard: React.FC = ({ poolInfo }: Props) => { MOBI rate - {totalMobiRate - ? totalMobiRate?.multiply(BIG_INT_SECONDS_IN_WEEK)?.toFixed(0, { groupSeparator: ',' }) ?? '-' - : '0'} + {mobiRate.multiply(BIG_INT_SECONDS_IN_WEEK)?.toFixed(0, { groupSeparator: ',' })} {' / week'} )} - {poolInfo.externalRewardRates && - poolInfo.externalRewardRates.map((rate) => ( + {meta.display.gauge?.additionalRewards.length !== 0 && + meta.display.gauge?.additionalRewards.map((rate) => ( {rate.token.symbol?.toUpperCase()} rate - {rate - ? rate?.multiply(BIG_INT_SECONDS_IN_WEEK)?.toSignificant(4, { groupSeparator: ',' }) ?? '-' - : '0'} + {rate.multiply(BIG_INT_SECONDS_IN_WEEK)?.toSignificant(4, { groupSeparator: ',' })} {` / week`} ))} - {connected && isStaking && ( + {connected && userDeposited.greaterThan(0) && ( Your share @@ -448,11 +366,7 @@ export const StablePoolCard: React.FC = ({ poolInfo }: Props) => { ) .join(', ')} /> */} - - {!pegComesAfter && peggedTo} - {valueOfDeposited.toFixed(displayDecimals + 2)} - {pegComesAfter && ` ${peggedTo}`} - + {totalDisplay(userDeposited)} )} @@ -463,8 +377,12 @@ export const StablePoolCard: React.FC = ({ poolInfo }: Props) => { (isStaking ? setOpenManage(true) : setOpenDeposit(true)) : connect} - eth={coin === Coins.Ether} + onClick={ + connected + ? () => (userDeposited.greaterThan(0) ? setOpenManage(true) : setOpenDeposit(true)) + : connect + } + eth={meta.display.peg.coin === Coins.Ether} style={{ width: '10%', fontWeight: 700, @@ -473,25 +391,25 @@ export const StablePoolCard: React.FC = ({ poolInfo }: Props) => { marginTop: '-20px', }} > - {isStaking ? 'MANAGE' : 'DEPOSIT'} + {userDeposited.greaterThan(0) ? 'MANAGE' : 'DEPOSIT'} )}
- {!isStaking && (openManage || isMobile) && ( + {!userDeposited.greaterThan(0) && (openManage || isMobile) && ( setOpenDeposit(true) : connect} - eth={coin === Coins.Ether} + eth={meta.display.peg.coin === Coins.Ether} style={{ fontWeight: 700, fontSize: 18 }} > DEPOSIT )} - {connected && isStaking && (openManage || isMobile) && ( + {connected && userDeposited.greaterThan(0) && (openManage || isMobile) && (
= ({ poolInfo }: Props) => { gap: isMobile ? '0.25rem' : '1rem', flexWrap: 'wrap', padding: isMobile ? 0 : '1rem', - paddingBottom: isMobile && '0', + paddingBottom: '0', marginLeft: 'auto', marginRight: 'auto', }} @@ -510,6 +428,7 @@ export const StablePoolCard: React.FC = ({ poolInfo }: Props) => { backgroundHover={theme(false).celoGreen} onClick={() => setOpenDeposit(true)} style={{ fontWeight: 700, fontSize: isMobile ? 15 : 18 }} + eth={false} > DEPOSIT @@ -518,15 +437,17 @@ export const StablePoolCard: React.FC = ({ poolInfo }: Props) => { backgroundHover={theme(false).celoRed} onClick={() => setOpenWithdraw(true)} style={{ fontWeight: 700, fontSize: isMobile ? 15 : 18 }} + eth={false} > WITHDRAW - {poolInfo.gaugeAddress !== undefined && ( + {meta.display.gauge !== null && ( history.push(`/farm/${poolInfo.poolAddress}`)} + onClick={() => history.push(`/farm/${meta.display.gauge?.address}`)} + eth={false} > FARM diff --git a/src/components/earn/StakingModal.tsx b/src/components/earn/StakingModal.tsx index 9a4a31a7c18..7d70515e90f 100644 --- a/src/components/earn/StakingModal.tsx +++ b/src/components/earn/StakingModal.tsx @@ -1,21 +1,23 @@ import { TransactionResponse } from '@ethersproject/providers' -import { JSBI, Token, TokenAmount } from '@ubeswap/sdk' +import CurrencyLogo from 'components/CurrencyLogo' import Loader from 'components/Loader' +import NumericalInput from 'components/NumericalInput' +import { useWeb3Context } from 'hooks' import { useMobi } from 'hooks/Tokens' +import JSBI from 'jsbi' +import { Token, TokenAmount } from 'lib/token-utils' import React, { useCallback, useState } from 'react' -import { StablePoolInfo } from 'state/stablePools/hooks' +import { tryParseAmount } from 'state/swap/hooks' +import { useTokenBalance } from 'state/wallet/hooks' import styled from 'styled-components' import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback' import { useLiquidityGaugeContract } from '../../hooks/useContract' import useTransactionDeadline from '../../hooks/useTransactionDeadline' -import { useDerivedStakeInfo } from '../../state/stake/hooks' import { useTransactionAdder } from '../../state/transactions/hooks' import { CloseIcon, TYPE } from '../../theme' -import { maxAmountSpend } from '../../utils/maxAmountSpend' import { ButtonConfirmed, ButtonError } from '../Button' import { AutoColumn } from '../Column' -import CurrencyInputPanel from '../CurrencyInputPanel' import Modal from '../Modal' import { LoadingView, SubmittedView } from '../ModalViews' import ProgressCircles from '../ProgressSteps' @@ -39,31 +41,45 @@ const ContentWrapper = styled(AutoColumn)` interface StakingModalProps { isOpen: boolean onDismiss: () => void - stakingInfo: StablePoolInfo - userLiquidityUnstaked: TokenAmount | undefined + userDeposited: TokenAmount + totalDeposited: TokenAmount + userLiquidityUnstaked: TokenAmount + gaugeAddress: string + mobiRate: TokenAmount } const calcNewRewardRate = (totalMobiRate: JSBI, totalStaked: JSBI, stakedByUser: JSBI, token: Token) => new TokenAmount(token, JSBI.multiply(totalMobiRate, JSBI.divide(stakedByUser, totalStaked))) -export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiquidityUnstaked }: StakingModalProps) { +export default function StakingModal({ + isOpen, + onDismiss, + userDeposited, + totalDeposited, + userLiquidityUnstaked, + gaugeAddress, + mobiRate, +}: StakingModalProps) { const addTransaction = useTransactionAdder() const mobi = useMobi() + const { connected } = useWeb3Context() // track and parse user input const [typedValue, setTypedValue] = useState('') - const { parsedAmount, error } = useDerivedStakeInfo(typedValue, stakingInfo.lpToken, userLiquidityUnstaked) - const parsedAmountWrapped = parsedAmount + + const inputAmount = tryParseAmount(typedValue, userDeposited.token) + // const { parsedAmount, error } = useDerivedStakeInfo(typedValue, stakingInfo.lpToken, userLiquidityUnstaked) + // const parsedAmountWrapped = parsedAmount let hypotheticalMobiRewardRate: TokenAmount = new TokenAmount(mobi, '0') - if (parsedAmountWrapped?.greaterThan('0')) { - if (stakingInfo.totalStakedAmount && stakingInfo.totalStakedAmount.equalTo('0')) { - hypotheticalMobiRewardRate = new TokenAmount(mobi, stakingInfo.mobiRate) + if (inputAmount?.greaterThan('0')) { + if (totalDeposited.equalTo('0')) { + hypotheticalMobiRewardRate = mobiRate } else { hypotheticalMobiRewardRate = calcNewRewardRate( - stakingInfo.mobiRate, - JSBI.add(stakingInfo.totalStakedAmount?.raw, parsedAmountWrapped.raw), - JSBI.add(stakingInfo.stakedAmount.raw, parsedAmountWrapped.raw), + mobiRate.raw, + JSBI.add(totalDeposited.raw, inputAmount.raw), + JSBI.add(userDeposited.raw, inputAmount.raw), mobi ) } @@ -80,16 +96,16 @@ export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiqui // approval data for stake const deadline = useTransactionDeadline() - const [approval, approveCallback] = useApproveCallback(parsedAmount, stakingInfo.gaugeAddress) + const [approval, approveCallback] = useApproveCallback(inputAmount, gaugeAddress) - const stakingContract = useLiquidityGaugeContract(stakingInfo.gaugeAddress) + const stakingContract = useLiquidityGaugeContract(gaugeAddress) const depositFunction = stakingContract?.['deposit(uint256)'] - async function onStake() { + const onStake = useCallback(async () => { setAttempting(true) - if (stakingContract && parsedAmount && deadline) { + if (stakingContract && inputAmount && deadline && depositFunction) { if (approval === ApprovalState.APPROVED) { - await depositFunction(parsedAmount.raw.toString(), { gasLimit: 10000000 }).then( + await depositFunction(inputAmount.raw.toString(), { gasLimit: 10000000 }).then( (response: TransactionResponse) => { addTransaction(response, { summary: `Stake deposited liquidity`, @@ -102,25 +118,15 @@ export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiqui throw new Error('Attempting to stake without approval or a signature. Please contact support.') } } - } + }, [addTransaction, approval, deadline, depositFunction, inputAmount, stakingContract]) - // wrapped onUserInput to clear signatures - const onUserInput = useCallback((typedValue: string) => { - setTypedValue(typedValue) - }, []) - - // used for max input button - const maxAmountInput = maxAmountSpend(userLiquidityUnstaked) - const atMaxAmount = Boolean(maxAmountInput && parsedAmount?.equalTo(maxAmountInput)) - const handleMax = useCallback(() => { - maxAmountInput && onUserInput(maxAmountInput.toExact()) - }, [maxAmountInput, onUserInput]) + let error: string | undefined + if (!connected) { + error = 'Connect Wallet' + } async function onAttemptToApprove() { if (!deadline) throw new Error('missing dependencies') - const liquidityAmount = parsedAmount - if (!liquidityAmount) throw new Error('missing liquidity amount') - approveCallback() } @@ -132,17 +138,7 @@ export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiqui Deposit - +
@@ -176,7 +172,7 @@ export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiqui {error ?? 'Deposit'} @@ -189,7 +185,7 @@ export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiqui Depositing Liquidity - {parsedAmount?.toSignificant(4)} MOBI LP + {inputAmount?.toSignificant(4)} MOBI LP )} @@ -197,10 +193,98 @@ export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiqui Transaction Submitted - Deposited {parsedAmount?.toSignificant(4)} MOBI LP + Deposited {inputAmount?.toSignificant(4)} MOBI LP )} ) } + +type CurrencyRowProps = { + tokenAmount: TokenAmount + setInput: (amount: string) => void + input: string +} + +const InputRow = styled.div` + ${({ theme }) => theme.flexRowNoWrap}; + align-items: center; + justify-content: space-between; + padding: 0.75rem 0.75rem 0.75rem 1rem; +` + +const InputDiv = styled.div` + display: flex; + min-width: 40%; +` + +const Aligner = styled.span` + display: flex; + align-items: center; + justify-content: space-between; +` + +const StyledTokenName = styled.span<{ active?: boolean }>` + ${({ active }) => (active ? ' margin: 0 0.25rem 0 0.75rem;' : ' margin: 0 0.25rem 0 0.25rem;')} + font-size: ${({ active }) => (active ? '20px' : '16px')}; + color: ${({ theme }) => theme.text1}; +` +const BalanceText = styled(TYPE.subHeader)` + cursor: pointer; +` + +const CurrencyRow = ({ tokenAmount, setInput, input }: CurrencyRowProps) => { + const { address, connected } = useWeb3Context() + const mobi = useMobi() + const currency = tokenAmount.token + const tokenBalance = useTokenBalance(connected ? address : undefined, currency ?? undefined) + + const decimalPlacesForBalance = tokenBalance?.greaterThan( + '1' //JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt(tokenBalance.token.decimals - 2)).toString() + ) + ? 2 + : tokenBalance?.greaterThan('0') + ? 6 + : 2 + + const mainRow = ( + +
+ + + + {(currency && currency.symbol && currency.symbol.length > 20 + ? currency.symbol.slice(0, 4) + + '...' + + currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length) + : currency?.symbol) || ''} + + +
+ + { + setInput(val) + }} + /> + +
+ ) + const balanceRow = ( +
+ setInput(tokenBalance?.toFixed(5) ?? '')}> + Balance: {tokenBalance?.toFixed(decimalPlacesForBalance) ?? 'Loading...'} + +
+ ) + + return ( +
+ {balanceRow} + {mainRow} +
+ ) +} diff --git a/src/components/earn/UnstakingModal.tsx b/src/components/earn/UnstakingModal.tsx index dad85d4aca7..a52f44c4b14 100644 --- a/src/components/earn/UnstakingModal.tsx +++ b/src/components/earn/UnstakingModal.tsx @@ -1,8 +1,6 @@ import { TransactionResponse } from '@ethersproject/providers' -import { TokenAmount } from '@ubeswap/sdk' -import { useMobi } from 'hooks/Tokens' -import React, { useState } from 'react' -import { StablePoolInfo } from 'state/stablePools/hooks' +import { TokenAmount } from 'lib/token-utils' +import React, { useCallback, useState } from 'react' import styled from 'styled-components' import { useWeb3Context } from '../../hooks' @@ -24,10 +22,11 @@ const ContentWrapper = styled(AutoColumn)` interface StakingModalProps { isOpen: boolean onDismiss: () => void - stakingInfo: StablePoolInfo + userDeposited: TokenAmount + gaugeAddress: string } -export default function UnstakingModal({ isOpen, onDismiss, stakingInfo }: StakingModalProps) { +export default function UnstakingModal({ isOpen, onDismiss, userDeposited, gaugeAddress }: StakingModalProps) { const { connected } = useWeb3Context() // monitor call to help UI loading state @@ -41,17 +40,13 @@ export default function UnstakingModal({ isOpen, onDismiss, stakingInfo }: Staki onDismiss() } - const stakingContract = useLiquidityGaugeContract(stakingInfo.gaugeAddress) - stakingContract?.['withdraw(uint256,bool)'] - const mobi = useMobi() - const { stakedAmount } = stakingInfo - const pendingMobi = new TokenAmount(mobi, stakingInfo.pendingMobi ?? '0') + const stakingContract = useLiquidityGaugeContract(gaugeAddress) const withdrawFunction = stakingContract?.['withdraw(uint256,bool)'] - async function onWithdraw() { - if (stakingContract && stakingInfo?.stakedAmount) { + const onWithdraw = useCallback(async () => { + if (withdrawFunction) { setAttempting(true) - await withdrawFunction(stakedAmount.raw.toString(), true, { gasLimit: 3000000 }) + await withdrawFunction(userDeposited.raw.toString(), true, { gasLimit: 3000000 }) .then((response: TransactionResponse) => { addTransaction(response, { summary: `Withdraw deposited liquidity`, @@ -63,15 +58,12 @@ export default function UnstakingModal({ isOpen, onDismiss, stakingInfo }: Staki console.log(error) }) } - } + }, [addTransaction, userDeposited.raw, withdrawFunction]) let error: string | undefined if (!connected) { error = 'Connect Wallet' } - if (!stakingInfo?.stakedAmount) { - error = error ?? 'Enter an amount' - } return ( @@ -81,34 +73,18 @@ export default function UnstakingModal({ isOpen, onDismiss, stakingInfo }: Staki Withdraw - {stakingInfo?.stakedAmount && ( - - - {} - - Deposited liquidity - - )} - {/* {pendingMobi && ( - - - {} - - Unclaimed MOBI - - )} */} - {/* {stakingInfo?.dualRewards && stakingInfo?.earnedAmount && ( - - - {} - - Unclaimed {stakingInfo?.rewardToken?.symbol} - - )} */} + + + + {} + + Deposited liquidity + + When you withdraw, your liquidity is removed from the mining pool. - + {error ?? 'Withdraw'} @@ -116,7 +92,7 @@ export default function UnstakingModal({ isOpen, onDismiss, stakingInfo }: Staki {attempting && !hash && ( - Withdrawing {stakingInfo?.stakedAmount?.toSignificant(4)} MOBI-LP + Withdrawing {userDeposited.toSignificant(4)} MOBI-LP )} diff --git a/src/components/earn/WithdrawLP.tsx b/src/components/earn/WithdrawLP.tsx index bcb0b2a6361..c2426bac055 100644 --- a/src/components/earn/WithdrawLP.tsx +++ b/src/components/earn/WithdrawLP.tsx @@ -1,6 +1,9 @@ import { TransactionResponse } from '@ethersproject/providers' -import { JSBI, Token, TokenAmount } from '@ubeswap/sdk' import CurrencyLogo from 'components/CurrencyLogo' +import JSBI from 'jsbi' +import { calculateEstimatedWithdrawAmount } from 'lib/calculator' +import { Token, TokenAmount } from 'lib/token-utils' +import { Meta } from 'pages/Pool' import React, { useState } from 'react' import styled from 'styled-components' @@ -8,7 +11,6 @@ import { useWeb3Context } from '../../hooks' import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback' import { useStableSwapContract } from '../../hooks/useContract' import useTransactionDeadline from '../../hooks/useTransactionDeadline' -import { StablePoolInfo, useExpectedTokens } from '../../state/stablePools/hooks' import { tryParseAmount } from '../../state/swap/hooks' import { useTransactionAdder } from '../../state/transactions/hooks' import { TYPE } from '../../theme' @@ -18,35 +20,33 @@ import { Input as NumericalInput } from '../NumericalInput' interface WithdrawModalProps { setAttempting: (attempting: boolean) => void setHash: (hash: string | undefined) => void - poolInfo: StablePoolInfo + meta: Meta } -export default function WithdrawLP({ poolInfo, setHash, setAttempting }: WithdrawModalProps) { +export default function WithdrawLP({ meta, setHash, setAttempting }: WithdrawModalProps) { const { connected } = useWeb3Context() - // monitor call to help UI loading state const addTransaction = useTransactionAdder() - const { tokens, lpToken } = poolInfo - const lpBalance = poolInfo.amountDeposited?.subtract(poolInfo.stakedAmount) const [approving, setApproving] = useState(false) const [input, setInput] = useState('') - const selectedAmount = tryParseAmount(input, lpToken) || new TokenAmount(lpToken, '0') - // const [selectedAmount, setSelectedAmount] = useState(new TokenAmount(lpToken, JSBI.BigInt('0'))) + const selectedAmount = + tryParseAmount(input, meta.display.pool.lpToken) || new TokenAmount(meta.display.pool.lpToken, 0) const deadline = useTransactionDeadline() - const expectedTokens = useExpectedTokens(poolInfo, selectedAmount) - const [approvalStatus, approvalCallback] = useApproveCallback(selectedAmount, poolInfo.poolAddress) - const stakingContract = useStableSwapContract(poolInfo.poolAddress) + const expectedAmount = calculateEstimatedWithdrawAmount({ poolTokenAmount: selectedAmount, ...meta.exchangeInfo }) + const [approvalStatus, approvalCallback] = useApproveCallback(selectedAmount, meta.display.pool.address) + const stakingContract = useStableSwapContract(meta.display.pool.address) + async function onWithdraw() { - if (stakingContract && poolInfo?.stakedAmount) { + if (stakingContract && deadline) { setAttempting(true) - const expected = expectedTokens.map((amount) => BigInt(amount.raw.toString())) + const expected = expectedAmount.withdrawAmounts.map((amount) => amount.raw.toString()) await stakingContract .removeLiquidity(selectedAmount.raw.toString(), expected, deadline, { gasLimit: 1000000 }) .then((response: TransactionResponse) => { addTransaction(response, { - summary: `Withdraw Liquidity from ${poolInfo.name}`, + summary: `Withdraw Liquidity from ${meta.display.name}`, }) setHash(response.hash) }) @@ -61,40 +61,44 @@ export default function WithdrawLP({ poolInfo, setHash, setAttempting }: Withdra if (!connected) { error = 'Connect Wallet' } - if (!poolInfo?.stakedAmount) { - error = error ?? 'Enter an amount' - } - if (selectedAmount.greaterThan(lpBalance || JSBI.BigInt('0'))) { + if (selectedAmount.greaterThan(meta.lpBalance)) { error = error ?? 'Insufficient Funds' } - const decimalPlacesForBalance = lpBalance?.greaterThan('1') ? 2 : lpBalance?.greaterThan('0') ? 10 : 2 + const decimalPlacesForBalance = meta.lpBalance?.greaterThan('1') ? 2 : meta.lpBalance?.greaterThan('0') ? 10 : 2 return ( <> - {poolInfo.stakedAmount.greaterThan(JSBI.BigInt(0)) && ( + {meta.userGauge && JSBI.greaterThan(meta.userGauge.balance, JSBI.BigInt(0)) && ( - {poolInfo.stakedAmount.toFixed(decimalPlacesForBalance)} MobLP currently deposited in the farm + {new TokenAmount(meta.display.pool.lpToken, meta.userGauge.balance).toFixed(decimalPlacesForBalance)} MobLP + currently deposited in the farm )} - + {selectedAmount.greaterThan(JSBI.BigInt('0')) && (
You will receive - {expectedTokens.map((tokenAmount, i) => ( + {expectedAmount.withdrawAmounts.map((tokenAmount, i) => ( <> null} + setTokenAmount={() => null} readOnly={true} - balance={lpBalance} + balance={meta.lpBalance} /> - {i !== expectedTokens.length - 1 && ( + {i !== expectedAmount.withdrawAmounts.length - 1 && ( + )} @@ -115,7 +119,7 @@ export default function WithdrawLP({ poolInfo, setHash, setAttempting }: Withdra )} {approvalStatus === ApprovalState.APPROVED && ( - + {error ?? 'Withdraw'} )} @@ -131,15 +135,11 @@ type CurrencyRowProps = { balance?: TokenAmount } -const InputRowLeft = styled.div`` - -const TokenInfo = styled.div`` - -const InputRow = styled.div<{ selected: boolean }>` +const InputRow = styled.div` ${({ theme }) => theme.flexRowNoWrap}; align-items: center; justify-content: space-between; - padding: ${({ selected }) => (selected ? '0.75rem 0.5rem 0.75rem 1rem' : '0.75rem 0.75rem 0.75rem 1rem')}; + padding: 0.75rem 0.75rem 0.75rem 1rem; ` const InputDiv = styled.div` @@ -153,22 +153,6 @@ const Aligner = styled.span` justify-content: space-between; ` -const InputPanel = styled.div<{ hideInput?: boolean }>` - ${({ theme }) => theme.flexColumnNoWrap} - position: relative; - border-radius: ${({ hideInput }) => (hideInput ? '8px' : '20px')}; - background-color: ${({ theme }) => theme.bg2}; - z-index: 1; - width: 100%; -` - -const Container = styled.div<{ hideInput: boolean }>` - border-radius: ${({ hideInput }) => (hideInput ? '8px' : '20px')}; - border: 1px solid ${({ theme }) => theme.bg2}; - background-color: ${({ theme }) => theme.bg1}; - padding: 0.5rem; -` - const StyledTokenName = styled.span<{ active?: boolean }>` ${({ active }) => (active ? ' margin: 0 0.25rem 0 0.75rem;' : ' margin: 0 0.25rem 0 0.25rem;')} font-size: ${({ active }) => (active ? '20px' : '16px')}; @@ -181,7 +165,6 @@ const BalanceText = styled(TYPE.subHeader)` const CurrencyRow = ({ val, token, setTokenAmount, balance, readOnly }: CurrencyRowProps) => { const currency = token const tokenBalance = balance - const TEN = JSBI.BigInt('10') const mainRow = ( @@ -226,11 +209,3 @@ const CurrencyRow = ({ val, token, setTokenAmount, balance, readOnly }: Currency
) } - -const insertDecimal = (tokenAmount: TokenAmount) => { - const { token } = tokenAmount - const amount = tokenAmount.divide( - new TokenAmount(token, JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt(token.decimals))) - ) - return amount.toFixed(2) -} diff --git a/src/components/earn/WithdrawModal.tsx b/src/components/earn/WithdrawModal.tsx index d9b06f74f2a..28fe6c43f44 100644 --- a/src/components/earn/WithdrawModal.tsx +++ b/src/components/earn/WithdrawModal.tsx @@ -1,10 +1,10 @@ import JSBI from 'jsbi' +import { Meta } from 'pages/Pool' import { darken } from 'polished' import React, { useState } from 'react' import styled from 'styled-components' import { useColor } from '../../hooks/useColor' -import { StablePoolInfo } from '../../state/stablePools/hooks' import { CloseIcon, StyledInternalLink, TYPE } from '../../theme' import { ButtonPrimary } from '../Button' import { AutoColumn } from '../Column' @@ -12,7 +12,6 @@ import Modal from '../Modal' import { LoadingView, SubmittedView } from '../ModalViews' import { RowBetween } from '../Row' import WithdrawLP from './WithdrawLP' -import WithdrawTokens from './WithdrawTokens' const ContentWrapper = styled(AutoColumn)` width: 100%; @@ -35,12 +34,10 @@ const DepositWithdrawBtn = styled(StyledButton)` interface WithdrawModalProps { isOpen: boolean onDismiss: () => void - poolInfo: StablePoolInfo + meta: Meta } -export default function WithdrawModal({ isOpen, onDismiss, poolInfo }: WithdrawModalProps) { - // monitor call to help UI loading state - const [byToken, setByToken] = useState(false) +export default function WithdrawModal({ isOpen, onDismiss, meta }: WithdrawModalProps) { const [hash, setHash] = useState() const [attempting, setAttempting] = useState(false) @@ -58,30 +55,17 @@ export default function WithdrawModal({ isOpen, onDismiss, poolInfo }: WithdrawM {!attempting && !hash && ( - Withdraw from {poolInfo.name} + Withdraw from {meta.display.name} - {/* - - - By Token Amount - - - - setByToken(!byToken)} /> - */} - {JSBI.greaterThan(JSBI.subtract(poolInfo.amountDeposited?.raw, poolInfo.stakedAmount.raw), JSBI.BigInt(0)) ? ( - byToken ? ( - - ) : ( - - ) + {meta.userGauge && !JSBI.equal(JSBI.BigInt(0), meta.userGauge?.balance) ? ( + ) : ( Withdraw from farm first - + void - setHash: (hash: string | undefined) => void - poolInfo: StablePoolInfo -} - -export default function WithdrawTokens({ poolInfo, setHash, setAttempting }: WithdrawModalProps) { - const { address, connected } = useWeb3Context() - - // monitor call to help UI loading state - const addTransaction = useTransactionAdder() - const { tokens, lpToken } = poolInfo - const [approving, setApproving] = useState(false) - const lpBalance = useTokenBalance(connected ? address : undefined, lpToken) - - const [selectedAmounts, setSelectedAmounts] = useState( - tokens.map((t) => new TokenAmount(t, JSBI.BigInt('0'))) - ) - const deadline = useTransactionDeadline() - - const expectedLPTokens = useExpectedLpTokens(poolInfo, selectedAmounts, false) - const withSlippage = JSBI.add(expectedLPTokens.raw, JSBI.divide(expectedLPTokens.raw, JSBI.BigInt('10'))) - const [approvalStatus, tryApprove] = useApproveCallback(expectedLPTokens, poolInfo.poolAddress) - - const stakingContract = useStableSwapContract(poolInfo.poolAddress) - async function onDeposit() { - if (stakingContract && poolInfo?.stakedAmount) { - setAttempting(true) - const amounts = selectedAmounts.map((amount) => BigInt(amount.raw.toString())) - await stakingContract - .removeLiquidityImbalance(amounts, withSlippage.toString(), deadline) - .then((response: TransactionResponse) => { - addTransaction(response, { - summary: `Remove ${selectedAmounts - .map((amount) => `${amount.toFixed(2)} ${amount.currency.symbol}`) - .join(', ')} from ${poolInfo.name}`, - }) - setHash(response.hash) - }) - .catch((error: any) => { - setAttempting(false) - console.log(error) - }) - } - } - - let error: string | undefined - if (!connected) { - error = 'Connect Wallet' - } - if (!poolInfo?.stakedAmount) { - error = error ?? 'Enter an amount' - } - - if (expectedLPTokens.greaterThan(lpBalance || JSBI.BigInt('0'))) { - error = error ?? 'Insufficient Funds' - } - - return ( - <> - {poolInfo.tokens.map((token, i) => ( -
- - setSelectedAmounts([ - ...selectedAmounts.slice(0, i), - val, - ...selectedAmounts.slice(i + 1, selectedAmounts.length), - ]) - } - /> - {i !== selectedAmounts.length - 1 && ( - + - )} -
- ))} - - Expected Lp Tokens Required: {expectedLPTokens.toFixed(2)} - - {expectedLPTokens.greaterThan(JSBI.BigInt('0')) && approvalStatus !== ApprovalState.APPROVED && ( -
- { - setApproving(true) - await tryApprove() - await new Promise((resolve) => setTimeout(resolve, 20000)) - setApproving(false) - }} - > - Approve LP Tokens - -
- )} - {approvalStatus === ApprovalState.APPROVED && ( - - {error ?? 'Withdraw'} - - )} - - ) -} - -type CurrencyRowProps = { - tokenAmount: TokenAmount - setTokenAmount: (tokenAmount: TokenAmount) => void - readOnly: boolean | undefined -} - -const InputRow = styled.div<{ selected: boolean }>` - ${({ theme }) => theme.flexRowNoWrap}; - align-items: center; - justify-content: space-between; - padding: ${({ selected }) => (selected ? '0.75rem 0.5rem 0.75rem 1rem' : '0.75rem 0.75rem 0.75rem 1rem')}; -` - -const InputDiv = styled.div` - display: flex; - min-width: 40%; -` - -const Aligner = styled.span` - display: flex; - align-items: center; - justify-content: space-between; -` - -const StyledTokenName = styled.span<{ active?: boolean }>` - ${({ active }) => (active ? ' margin: 0 0.25rem 0 0.75rem;' : ' margin: 0 0.25rem 0 0.25rem;')} - font-size: ${({ active }) => (active ? '20px' : '16px')}; - color: ${({ theme }) => theme.text1}; -` -const BalanceText = styled(TYPE.subHeader)` - cursor: pointer; -` - -const CurrencyRow = ({ tokenAmount, setTokenAmount, readOnly }: CurrencyRowProps) => { - const { address, connected } = useWeb3Context() - const currency = tokenAmount.currency - const tokenBalance = useTokenBalance(connected ? address : undefined, currency ?? undefined) - const TEN = JSBI.BigInt('10') - const ZERO_TOK = new TokenAmount(currency, JSBI.BigInt('0')) - - const scaledDown = (num: JSBI) => JSBI.divide(num, JSBI.exponentiate(TEN, JSBI.BigInt(currency.decimals))) - - const mainRow = ( - -
- - - - {(currency && currency.symbol && currency.symbol.length > 20 - ? currency.symbol.slice(0, 4) + - '...' + - currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length) - : currency?.symbol) || ''} - - -
- - { - const amount = tryParseAmount(val, currency) - setTokenAmount(amount || ZERO_TOK) - }} - /> - -
- ) - const balanceRow = !readOnly && ( -
- setTokenAmount(tokenBalance || ZERO_TOK)}> - Balance: {tokenBalance?.toFixed(2)} - -
- ) - - return ( -
- {balanceRow} - - {mainRow} -
- ) -} +// {import { TransactionResponse } from '@ethersproject/providers' +// import CurrencyLogo from 'components/CurrencyLogo' +// import JSBI from 'jsbi' +// import { TokenAmount } from 'lib/token-utils' +// import React, { useState } from 'react' +// import styled from 'styled-components' + +// import { useWeb3Context } from '../../hooks' +// import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback' +// import { useStableSwapContract } from '../../hooks/useContract' +// import useTransactionDeadline from '../../hooks/useTransactionDeadline' +// import { tryParseAmount } from '../../state/swap/hooks' +// import { useTransactionAdder } from '../../state/transactions/hooks' +// import { useTokenBalance } from '../../state/wallet/hooks' +// import { TYPE } from '../../theme' +// import { ButtonError, ButtonPrimary } from '../Button' +// import { Input as NumericalInput } from '../NumericalInput' + +// interface WithdrawModalProps { +// setAttempting: (attempting: boolean) => void +// setHash: (hash: string | undefined) => void +// poolInfo: StablePoolInfo +// } + +// export default function WithdrawTokens({ poolInfo, setHash, setAttempting }: WithdrawModalProps) { +// const { address, connected } = useWeb3Context() + +// // monitor call to help UI loading state +// const addTransaction = useTransactionAdder() +// const { tokens, lpToken } = poolInfo +// const [approving, setApproving] = useState(false) +// const lpBalance = useTokenBalance(connected ? address : undefined, lpToken) + +// const [selectedAmounts, setSelectedAmounts] = useState( +// tokens.map((t) => new TokenAmount(t, JSBI.BigInt('0'))) +// ) +// const deadline = useTransactionDeadline() + +// const expectedLPTokens = useExpectedLpTokens(poolInfo, selectedAmounts, false) +// const withSlippage = JSBI.add(expectedLPTokens.raw, JSBI.divide(expectedLPTokens.raw, JSBI.BigInt('10'))) +// const [approvalStatus, tryApprove] = useApproveCallback(expectedLPTokens, poolInfo.poolAddress) + +// const stakingContract = useStableSwapContract(poolInfo.poolAddress) +// async function onDeposit() { +// if (stakingContract && poolInfo?.stakedAmount) { +// setAttempting(true) +// const amounts = selectedAmounts.map((amount) => BigInt(amount.raw.toString())) +// await stakingContract +// .removeLiquidityImbalance(amounts, withSlippage.toString(), deadline) +// .then((response: TransactionResponse) => { +// addTransaction(response, { +// summary: `Remove ${selectedAmounts +// .map((amount) => `${amount.toFixed(2)} ${amount.currency.symbol}`) +// .join(', ')} from ${poolInfo.name}`, +// }) +// setHash(response.hash) +// }) +// .catch((error: any) => { +// setAttempting(false) +// console.log(error) +// }) +// } +// } + +// let error: string | undefined +// if (!connected) { +// error = 'Connect Wallet' +// } +// if (!poolInfo?.stakedAmount) { +// error = error ?? 'Enter an amount' +// } + +// if (expectedLPTokens.greaterThan(lpBalance || JSBI.BigInt('0'))) { +// error = error ?? 'Insufficient Funds' +// } + +// return ( +// <> +// {poolInfo.tokens.map((token, i) => ( +//
+// +// setSelectedAmounts([ +// ...selectedAmounts.slice(0, i), +// val, +// ...selectedAmounts.slice(i + 1, selectedAmounts.length), +// ]) +// } +// /> +// {i !== selectedAmounts.length - 1 && ( +// + +// )} +//
+// ))} +// +// Expected Lp Tokens Required: {expectedLPTokens.toFixed(2)} +// +// {expectedLPTokens.greaterThan(JSBI.BigInt('0')) && approvalStatus !== ApprovalState.APPROVED && ( +//
+// { +// setApproving(true) +// await tryApprove() +// await new Promise((resolve) => setTimeout(resolve, 20000)) +// setApproving(false) +// }} +// > +// Approve LP Tokens +// +//
+// )} +// {approvalStatus === ApprovalState.APPROVED && ( +// +// {error ?? 'Withdraw'} +// +// )} +// +// ) +// } + +// type CurrencyRowProps = { +// tokenAmount: TokenAmount +// setTokenAmount: (tokenAmount: TokenAmount) => void +// readOnly: boolean | undefined +// } + +// const InputRow = styled.div<{ selected: boolean }>` +// ${({ theme }) => theme.flexRowNoWrap}; +// align-items: center; +// justify-content: space-between; +// padding: ${({ selected }) => (selected ? '0.75rem 0.5rem 0.75rem 1rem' : '0.75rem 0.75rem 0.75rem 1rem')}; +// ` + +// const InputDiv = styled.div` +// display: flex; +// min-width: 40%; +// ` + +// const Aligner = styled.span` +// display: flex; +// align-items: center; +// justify-content: space-between; +// ` + +// const StyledTokenName = styled.span<{ active?: boolean }>` +// ${({ active }) => (active ? ' margin: 0 0.25rem 0 0.75rem;' : ' margin: 0 0.25rem 0 0.25rem;')} +// font-size: ${({ active }) => (active ? '20px' : '16px')}; +// color: ${({ theme }) => theme.text1}; +// ` +// const BalanceText = styled(TYPE.subHeader)` +// cursor: pointer; +// ` + +// const CurrencyRow = ({ tokenAmount, setTokenAmount, readOnly }: CurrencyRowProps) => { +// const { address, connected } = useWeb3Context() +// const currency = tokenAmount.currency +// const tokenBalance = useTokenBalance(connected ? address : undefined, currency ?? undefined) +// const TEN = JSBI.BigInt('10') +// const ZERO_TOK = new TokenAmount(currency, JSBI.BigInt('0')) + +// const scaledDown = (num: JSBI) => JSBI.divide(num, JSBI.exponentiate(TEN, JSBI.BigInt(currency.decimals))) + +// const mainRow = ( +// +//
+// +// +// +// {(currency && currency.symbol && currency.symbol.length > 20 +// ? currency.symbol.slice(0, 4) + +// '...' + +// currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length) +// : currency?.symbol) || ''} +// +// +//
+// +// { +// const amount = tryParseAmount(val, currency) +// setTokenAmount(amount || ZERO_TOK) +// }} +// /> +// +//
+// ) +// const balanceRow = !readOnly && ( +//
+// setTokenAmount(tokenBalance || ZERO_TOK)}> +// Balance: {tokenBalance?.toFixed(2)} +// +//
+// ) + +// return ( +//
+// {balanceRow} + +// {mainRow} +//
+// ) +// } +export {} diff --git a/src/components/swap/ConfirmSwapModal.tsx b/src/components/swap/ConfirmSwapModal.tsx index c25dbddacb8..705838f2380 100644 --- a/src/components/swap/ConfirmSwapModal.tsx +++ b/src/components/swap/ConfirmSwapModal.tsx @@ -1,4 +1,3 @@ -import { currencyEquals } from '@ubeswap/sdk' import React, { useCallback, useMemo } from 'react' import { useLocation } from 'react-router' import { MentoTrade } from 'state/mento/hooks' @@ -19,9 +18,9 @@ import SwapModalHeader from './SwapModalHeader' */ function tradeMeaningfullyDiffers(tradeA: MobiusTrade | MentoTrade, tradeB: MobiusTrade | MentoTrade): boolean { return ( - !currencyEquals(tradeA.input.currency, tradeB.input.currency) || + !tradeA.input.token.equals(tradeB.input.token) || !tradeA.input.equalTo(tradeB.input) || - !currencyEquals(tradeA.output.currency, tradeB.output.currency) || + !tradeA.output.token.equals(tradeB.output.token) || !tradeA.output.equalTo(tradeB.output) ) } @@ -87,8 +86,8 @@ export default function ConfirmSwapModal({ // text to show while loading const pendingText = `Swapping ${trade?.input?.toSignificant(6)} ${ - trade?.input?.currency?.symbol - } for ${trade?.output?.toSignificant(6)} ${trade?.output?.currency?.symbol}` + trade?.input?.token?.symbol + } for ${trade?.output?.toSignificant(6)} ${trade?.output?.token?.symbol}` const confirmationContent = useCallback( () => diff --git a/src/components/swap/FormattedPriceImpact.tsx b/src/components/swap/FormattedPriceImpact.tsx index 02b539eb6d0..095b8b48104 100644 --- a/src/components/swap/FormattedPriceImpact.tsx +++ b/src/components/swap/FormattedPriceImpact.tsx @@ -1,4 +1,4 @@ -import { Percent } from '@ubeswap/sdk' +import { Percent } from 'lib/token-utils' import React from 'react' import { ONE_BIPS } from '../../constants' diff --git a/src/components/swap/SwapModalHeader.tsx b/src/components/swap/SwapModalHeader.tsx index a08656ee0f2..0f066c22923 100644 --- a/src/components/swap/SwapModalHeader.tsx +++ b/src/components/swap/SwapModalHeader.tsx @@ -42,7 +42,7 @@ export default function SwapModalHeader({ - + - {trade.input.currency.symbol} + {trade.input.token.symbol} @@ -62,7 +62,7 @@ export default function SwapModalHeader({ - + - {trade.output.currency.symbol} + {trade.output.token.symbol} @@ -104,7 +104,7 @@ export default function SwapModalHeader({ {`Output is estimated. You will receive at least `} - {slippageAdjustedAmounts[Field.OUTPUT]?.toSignificant(6)} {trade.output.currency.symbol} + {slippageAdjustedAmounts[Field.OUTPUT]?.toSignificant(6)} {trade.output.token.symbol} {' or the transaction will revert.'} @@ -112,7 +112,7 @@ export default function SwapModalHeader({ {`Input is estimated. You will sell at most `} - {slippageAdjustedAmounts[Field.INPUT]?.toSignificant(6)} {trade.input.currency.symbol} + {slippageAdjustedAmounts[Field.INPUT]?.toSignificant(6)} {trade.input.token.symbol} {' or the transaction will revert.'} diff --git a/src/components/swap/TradePrice.tsx b/src/components/swap/TradePrice.tsx index 1d5a768bd2d..14b07de02cc 100644 --- a/src/components/swap/TradePrice.tsx +++ b/src/components/swap/TradePrice.tsx @@ -1,4 +1,4 @@ -import { Price } from '@ubeswap/sdk' +import { Price } from 'lib/token-utils' import React, { useContext } from 'react' import { Repeat } from 'react-feather' import { Text } from 'rebass' diff --git a/src/components/swap/routing/TradeDetails/MobiusTradeDetails.tsx b/src/components/swap/routing/TradeDetails/MobiusTradeDetails.tsx index cebcc1977d9..cfed1e2a1c1 100644 --- a/src/components/swap/routing/TradeDetails/MobiusTradeDetails.tsx +++ b/src/components/swap/routing/TradeDetails/MobiusTradeDetails.tsx @@ -34,8 +34,8 @@ export const MobiusTradeDetails: React.FC = ({ trade, allowedSlippage }: {isExactIn - ? `${slippageAdjustedAmounts[Field.OUTPUT]?.toSignificant(4)} ${trade.output.currency.symbol}` ?? '-' - : `${slippageAdjustedAmounts[Field.INPUT]?.toSignificant(4)} ${trade.input.currency.symbol}` ?? '-'} + ? `${slippageAdjustedAmounts[Field.OUTPUT]?.toSignificant(4)} ${trade.output.token.symbol}` ?? '-' + : `${slippageAdjustedAmounts[Field.INPUT]?.toSignificant(4)} ${trade.input.token.symbol}` ?? '-'} @@ -57,7 +57,7 @@ export const MobiusTradeDetails: React.FC = ({ trade, allowedSlippage }: - {realizedLPFee ? `${realizedLPFee.toSignificant(4)} ${trade.input.currency.symbol}` : '-'} + {realizedLPFee ? `${realizedLPFee.toSignificant(4)} ${trade.input.token.symbol}` : '-'} diff --git a/src/connectors/index.ts b/src/connectors/index.ts index a5f3f2e0677..a9c4c8b7658 100644 --- a/src/connectors/index.ts +++ b/src/connectors/index.ts @@ -1,10 +1,14 @@ -import { ChainId, parseNetwork } from '@ubeswap/sdk' +import { ChainId } from 'lib/token-utils' + +function parseNetwork(chainId: number): ChainId { + return chainId === 62320 ? ChainId.Baklava : chainId === 44787 ? ChainId.Alfajores : ChainId.Mainnet +} const networkChainIDFromHostname: ChainId = window.location.hostname.includes('alfajores') - ? ChainId.ALFAJORES + ? ChainId.Alfajores : window.location.hostname.includes('baklava') - ? ChainId.BAKLAVA - : ChainId.MAINNET + ? ChainId.Baklava + : ChainId.Mainnet export const NETWORK_CHAIN_ID: ChainId = process.env.REACT_APP_CHAIN_ID ? parseNetwork(parseInt(process.env.REACT_APP_CHAIN_ID)) diff --git a/src/constants/ConstantSum.ts b/src/constants/ConstantSum.ts index b37f51f4e90..d205410cfbe 100644 --- a/src/constants/ConstantSum.ts +++ b/src/constants/ConstantSum.ts @@ -1,125 +1,28 @@ -import { ChainId } from '@ubeswap/sdk' -import { WrappedTokenInfo } from 'state/lists/hooks' +import { ChainId, Token } from 'lib/token-utils' + +import { USDC, USDC1, WBTC, WBTC1, WETH, WETH1 } from './tokens' export type ConstantSumInfo = { address: string - tokens: [WrappedTokenInfo, WrappedTokenInfo] + tokens: [Token, Token] } export const ConstantSum: { [K in ChainId]: ConstantSumInfo[] } = { - [ChainId.MAINNET]: [ + [ChainId.Mainnet]: [ { // wETH v1/v2 pool address: '0xb1a0BDe36341065cA916c9f5619aCA82A43659A3', - tokens: [ - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0xE919F65739c26a42616b7b8eedC6b5524d1e3aC4', - decimals: 18, - symbol: 'wETHxV1', - name: 'Wrapped Ether (Optics Bridge)', - logoURI: 'https://etherscan.io/token/images/weth_28.png', - }, - [] - ), - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x122013fd7dF1C6F636a5bb8f03108E876548b455', - decimals: 18, - symbol: 'wETH', - name: 'Wrapped Ether (Optics Bridge)', - logoURI: 'https://etherscan.io/token/images/weth_28.png', - }, - [] - ), - ], + tokens: [WETH1[ChainId.Mainnet], WETH[ChainId.Mainnet]], }, - // { - // // wETH v1/v2 pool - // address: '0x7e0B5284864916A866Fc391454ac2f452F91a336', - // tokens: [ - // new WrappedTokenInfo( - // { - // chainId: ChainId.MAINNET, - // address: '0xD68536297a01DBB4739a4e2cC1E79a8CFA2E3A3E', - // decimals: 18, - // symbol: 'wETHxV1', - // name: 'Wrapped Ether (Optics Bridge)', - // logoURI: 'https://etherscan.io/token/images/weth_28.png', - // }, - // [] - // ), - // new WrappedTokenInfo( - // { - // chainId: ChainId.MAINNET, - // address: '0xb909F71b53C621e467Ee9ECD387E6662CA4f15eF', - // decimals: 18, - // symbol: 'wETH', - // name: 'Wrapped Ether (Optics Bridge)', - // logoURI: 'https://etherscan.io/token/images/weth_28.png', - // }, - // [] - // ), - // ], - // }, - { // BTC v1/v2 address: '0xd5ab1BA8b2Ec70752068d1d728e728eAd0E19CBA', - tokens: [ - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0xBe50a3013A1c94768A1ABb78c3cB79AB28fc1aCE', - decimals: 8, - symbol: 'wBTCxV1', - name: 'Wrapped Bitcoin (Optics Bridge)', - logoURI: 'https://etherscan.io/token/images/wbtc_28.png?v=1', - }, - [] - ), - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0xBAAB46E28388d2779e6E31Fd00cF0e5Ad95E327B', - decimals: 8, - symbol: 'wBTC', - name: 'Wrapped Bitcoin (Optics Bridge)', - logoURI: 'https://etherscan.io/token/images/wbtc_28.png?v=1', - }, - [] - ), - ], + tokens: [WBTC1[ChainId.Mainnet], WBTC[ChainId.Mainnet]], }, { // USDC v1/v2 address: '0x70bfA1C8Ab4e42B9BE74f65941EFb6e5308148c7', - tokens: [ - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x2A3684e9Dc20B857375EA04235F2F7edBe818FA7', - decimals: 6, - symbol: 'cUSDCxV1', - name: 'US Dollar Coin (Optics Bridge)', - logoURI: 'https://bit.ly/3CwGimW', - }, - [] - ), - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0xef4229c8c3250C675F21BCefa42f58EfbfF6002a', - decimals: 6, - symbol: 'cUSDC', - name: 'US Dollar Coin (Optics Bridge)', - logoURI: 'https://bit.ly/3CwGimW', - }, - [] - ), - ], + tokens: [USDC1[ChainId.Mainnet], USDC[ChainId.Mainnet]], }, // { // // TEST pool @@ -150,6 +53,6 @@ export const ConstantSum: { [K in ChainId]: ConstantSumInfo[] } = { // ], // }, ], - [ChainId.ALFAJORES]: [], - [ChainId.BAKLAVA]: [], + [ChainId.Alfajores]: [], + [ChainId.Baklava]: [], } diff --git a/src/constants/NetworkInfo.ts b/src/constants/NetworkInfo.ts index 8ec0ca9ce67..41e4816757d 100644 --- a/src/constants/NetworkInfo.ts +++ b/src/constants/NetworkInfo.ts @@ -1,4 +1,4 @@ -import { ChainId } from '@ubeswap/sdk' +import { ChainId } from 'lib/token-utils' export function getExplorerLink( chainId: ChainId, diff --git a/src/constants/StablePools.ts b/src/constants/StablePools.ts index 826c024ee79..f7b7ef91a10 100644 --- a/src/constants/StablePools.ts +++ b/src/constants/StablePools.ts @@ -1,1269 +1,1111 @@ -import { ChainId, JSBI, Token } from '@ubeswap/sdk' -import BigNumber from 'bignumber.js' -import { VestType } from 'state/claim/reducer' -import { WrappedTokenInfo } from 'state/lists/hooks' - -import celoLogo from '../assets/images/celo-chain-logo.png' -import ethLogo from '../assets/images/ethereum-chain-logo.png' -import polygonLogo from '../assets/images/polygon-chain-logo.png' -import terraLogo from '../assets/images/terra-logo.png' - -const mobiToken = (chainId: number, address: string) => - new WrappedTokenInfo( - { - chainId, - address, - decimals: 18, - symbol: 'MOBI', - name: 'Mobius DAO Token', - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_MOBI.png', - }, - [] - ) - -export enum Coins { - Bitcoin, - Ether, - USD, - Celo, - Eur, -} - -export enum Chain { - Celo, - Ethereum, - Polygon, - Solana, - Avax, - All, - Terra, - Other, -} - -enum WarningType { - POOF = 'poof', -} - -type StableSwapMathConstants = { - name: string - rates: JSBI[] - lendingPrecision: JSBI - precision: JSBI - feeDenominator: JSBI - precisionMul: JSBI[] - feeIndex: number - decimals: JSBI[] - swapFee: JSBI -} -type StableSwapConstants = StableSwapMathConstants & { - tokens: WrappedTokenInfo[] - tokenAddresses: string[] - address: string - gaugeAddress: string - lpToken: Token - peggedTo: string - pegComesAfter: boolean | undefined - displayDecimals: number - additionalRewards?: string[] - additionalRewardRate?: string[] - lastClaim?: Date - displayChain: Chain - coin: Coins - disabled?: boolean - metaPool?: string - isKilled?: boolean - warningType?: WarningType -} - -export const ChainLogo: { [c in Chain]: string } = { - [Chain.Celo]: celoLogo, - [Chain.Ethereum]: ethLogo, - [Chain.Polygon]: polygonLogo, - [Chain.Solana]: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_SOL.png', - [Chain.Avax]: 'https://s2.coinmarketcap.com/static/img/coins/64x64/5805.png', - [Chain.All]: '', - [Chain.Terra]: terraLogo, - [Chain.Other]: '', -} - -export const PRICE: { [c in Coins]: number } = { - [Coins.Bitcoin]: 38000, - [Coins.Ether]: 2700, - [Coins.USD]: 1, - [Coins.Celo]: 6, - [Coins.Eur]: 1.17, -} - -export const MOBIUS_MINTER_ADDRESS: { [K in ChainId]: string } = { - [ChainId.MAINNET]: '0x5F0200CA03196D5b817E2044a0Bb0D837e0A7823', - [ChainId.ALFAJORES]: '0x5c212FA1cf8b1143f2142C26161e65404034c01f', - [ChainId.BAKLAVA]: '', -} - -export const MOBI_TOKEN: { [K in ChainId]: Token | undefined } = { - [ChainId.MAINNET]: mobiToken(ChainId.MAINNET, '0x73a210637f6F6B7005512677Ba6B3C96bb4AA44B'), - [ChainId.ALFAJORES]: mobiToken(ChainId.ALFAJORES, '0x6dDcbC22c1ED5D0662635ffb020c82DF4e1Ba234'), - [ChainId.BAKLAVA]: undefined, -} - -export const GAUGE_CONTROLLER: { [K in ChainId]: string } = { - [ChainId.MAINNET]: '0x7530E03056D3a8eD0323e61091ea2f17a1aC5C25', - [ChainId.ALFAJORES]: '0x00063Fbe0c90834EE90C6191d0D9F04eaB01A14f', - [ChainId.BAKLAVA]: '', -} - -export const GAUGE_PROXY: { [K in ChainId]: string } = { - [ChainId.MAINNET]: '0x0a3Ac12422C95F84b5bD18A6d9904d132a161C68', - [ChainId.ALFAJORES]: '', - [ChainId.BAKLAVA]: '', -} -const weeklyEmissionToSeconds = (n: number) => { - const yearlyEmission = new BigNumber(`${n}e+18`).dividedBy(7 * 24 * 60 * 60) - return yearlyEmission.toFixed(0) -} - -export const STATIC_POOL_INFO: { [K in ChainId]: StableSwapConstants[] } = { - [ChainId.MAINNET]: [ - { - name: 'UST (Allbridge)', - tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0xEd193C4E69F591E42398eF54DEa65aa1bb02835c'], - tokens: [ - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', - decimals: 18, - symbol: 'cUSD', - name: 'Celo Dollar', - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', - }, - [] - ), - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0xEd193C4E69F591E42398eF54DEa65aa1bb02835c', - decimals: 18, - symbol: 'UST', - name: 'Allbridge UST', - logoURI: 'https://raw.githubusercontent.com/kyscott18/default-token-list/master/assets/asset_UST.png', - }, - [] - ), - ], - address: '0x9F4AdBD0af281C69a582eB2E6fa2A594D4204CAe', - lpToken: new Token( - ChainId.MAINNET, - '0x9438e7281D7E3e99A9dD21e0EAd9c6a254e17ab2', - 18, - 'MobLP', - 'Mobius cUSD/aUST LP' - ), - swapFee: JSBI.multiply(JSBI.BigInt('2'), JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7'))), - rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - lendingPrecision: JSBI.BigInt('1'), - precision: JSBI.BigInt('18'), - feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - feeIndex: 0, - decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], - peggedTo: '$', - pegComesAfter: false, - displayDecimals: 0, - gaugeAddress: '0x107F94409746E8c8E6eFF139A100D17D9ca7FdfE', - additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], - additionalRewardRate: [weeklyEmissionToSeconds(2268)], // ['14776041660000000'], //['18468900000000000'], // ['7302827380000000'] - displayChain: Chain.Terra, - coin: Coins.USD, - }, - { - name: 'USDC (Optics V2)', - tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0xef4229c8c3250C675F21BCefa42f58EfbfF6002a'], - tokens: [ - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', - decimals: 18, - symbol: 'cUSD', - name: 'Celo Dollar', - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', - }, - [] - ), - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0xef4229c8c3250C675F21BCefa42f58EfbfF6002a', - decimals: 6, - symbol: 'cUSDC', - name: 'US Dollar Coin (Optics Bridge)', - logoURI: 'https://bit.ly/3CwGimW', - }, - [] - ), - ], - address: '0x9906589Ea8fd27504974b7e8201DF5bBdE986b03', - lpToken: new Token( - ChainId.MAINNET, - '0x39b6F09ef97dB406ab78D869471adb2384C494E3', - 18, - 'MobLP', - 'Mobius cUSD/cUSDC LP' - ), - swapFee: JSBI.multiply(JSBI.BigInt('2'), JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7'))), - rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - lendingPrecision: JSBI.BigInt('1'), - precision: JSBI.BigInt('18'), - feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - feeIndex: 0, - decimals: [JSBI.BigInt('18'), JSBI.BigInt('6')], - peggedTo: '$', - pegComesAfter: false, - displayDecimals: 0, - gaugeAddress: '0xc96AeeaFF32129da934149F6134Aa7bf291a754E', - additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], - additionalRewardRate: [weeklyEmissionToSeconds(11088)], // ['29552083330000000'], // ['36940104160000000'], // ['7302827380000000'] - displayChain: Chain.Ethereum, - coin: Coins.USD, - }, - { - name: 'DAI (Optics V2)', - tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0x90Ca507a5D4458a4C6C6249d186b6dCb02a5BCCd'], - tokens: [ - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', - decimals: 18, - symbol: 'cUSD', - name: 'Celo Dollar', - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', - }, - [] - ), - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x90Ca507a5D4458a4C6C6249d186b6dCb02a5BCCd', - decimals: 18, - symbol: 'DAI', - name: 'Optics DAI', - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_DAI.png', - }, - [] - ), - ], - address: '0xF3f65dFe0c8c8f2986da0FEc159ABE6fd4E700B4', - lpToken: new Token( - ChainId.MAINNET, - '0x274DD2dF039f1f6131419C82173D97770e6af6B7', - 18, - 'MobLP', - 'Mobius cUSD/cDAI LP' - ), - swapFee: JSBI.multiply(JSBI.BigInt('2'), JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7'))), - rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - lendingPrecision: JSBI.BigInt('1'), - precision: JSBI.BigInt('18'), - feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - feeIndex: 0, - decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], - peggedTo: '$', - pegComesAfter: false, - displayDecimals: 0, - gaugeAddress: '0xE1f9D952EecC07cfEFa69df9fBB0cEF260957119', - additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], - additionalRewardRate: [weeklyEmissionToSeconds(3780)], // ['14776041660000000'], //['18468900000000000'], // ['7302827380000000'] - displayChain: Chain.Ethereum, - coin: Coins.USD, - }, - { - name: 'WETH (Optics V2)', - tokenAddresses: ['0x2DEf4285787d58a2f811AF24755A8150622f4361', '0x122013fd7dF1C6F636a5bb8f03108E876548b455'], - tokens: [ - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x2DEf4285787d58a2f811AF24755A8150622f4361', - decimals: 18, - symbol: 'cETH', - name: 'Wrapped Ethereum', - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cETH.svg', - }, - [] - ), - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x122013fd7dF1C6F636a5bb8f03108E876548b455', - decimals: 18, - symbol: 'wETH', - name: 'Wrapped Ether (Optics Bridge)', - logoURI: 'https://etherscan.io/token/images/weth_28.png', - }, - [] - ), - ], - address: '0x74ef28D635c6C5800DD3Cd62d4c4f8752DaACB09', - lpToken: new Token( - ChainId.MAINNET, - '0x4fF08e2a4E7114af4B575AeF9250144f95790982', - 18, - 'MobLP', - 'Mobius cETH/wETH LP' - ), - swapFee: JSBI.multiply(JSBI.BigInt('2'), JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7'))), - rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - lendingPrecision: JSBI.BigInt('1'), - precision: JSBI.BigInt('18'), - feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - feeIndex: 0, - decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], - peggedTo: 'Ξ', - pegComesAfter: true, - displayDecimals: 2, - gaugeAddress: '0x487c30CB18AA9Ced435911E2B414e0e85D7E52bB', - displayChain: Chain.Ethereum, - coin: Coins.Ether, - additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], - additionalRewardRate: ['0'], // ['3694010416000000'], // ['7302827380000000'] - }, - { - name: 'wBTC (Optics V2)', - tokenAddresses: ['0xD629eb00dEced2a080B7EC630eF6aC117e614f1b', '0xBAAB46E28388d2779e6E31Fd00cF0e5Ad95E327B'], - tokens: [ - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0xD629eb00dEced2a080B7EC630eF6aC117e614f1b', - decimals: 18, - symbol: 'cBTC', - name: 'Wrapped Bitcoin', - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cBTC.png', - }, - [] - ), - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0xBAAB46E28388d2779e6E31Fd00cF0e5Ad95E327B', - decimals: 8, - symbol: 'wBTC', - name: 'Wrapped Bitcoin (Optics Bridge)', - logoURI: 'https://etherscan.io/token/images/wbtc_28.png?v=1', - }, - [] - ), - ], - address: '0xaEFc4e8cF655a182E8346B24c8AbcE45616eE0d2', - lpToken: new Token( - ChainId.MAINNET, - '0x20d7274C5aF4f9DE6e8C93025e44aF3979d9Ab2b', - 18, - 'MobLP', - 'Mobius cBTC/wBTC LP' - ), - swapFee: JSBI.multiply(JSBI.BigInt('2'), JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7'))), - rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - lendingPrecision: JSBI.BigInt('1'), - precision: JSBI.BigInt('18'), - feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - feeIndex: 0, - decimals: [JSBI.BigInt('18'), JSBI.BigInt('8')], - peggedTo: '₿', - pegComesAfter: true, - displayDecimals: 2, - gaugeAddress: '0x127b524c74C2162Ee4BB2e42d8d2eB9050C0293E', - displayChain: Chain.Ethereum, - coin: Coins.Bitcoin, - additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], - additionalRewardRate: ['0'], // ['3694010416000000'], // ['7302827380000000'] - }, - { - name: 'pUSDC (Optics V2)', - tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0x1bfc26cE035c368503fAE319Cc2596716428ca44'], - tokens: [ - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', - decimals: 18, - symbol: 'cUSD', - name: 'Celo Dollar', - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', - }, - [] - ), - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x1bfc26cE035c368503fAE319Cc2596716428ca44', - decimals: 6, - symbol: 'pUSDC', - name: 'USD Coin (PoS Optics)', - logoURI: 'https://bit.ly/3CwGimW', - }, - [] - ), - ], - address: '0xcCe0d62Ce14FB3e4363Eb92Db37Ff3630836c252', - lpToken: new Token( - ChainId.MAINNET, - '0x68b239b415970dD7a5234A9701cbB5BfaB544C7C', - 18, - 'MobLP', - 'Mobius cUSD/pUSDC LP' - ), - swapFee: JSBI.multiply(JSBI.BigInt('2'), JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7'))), - rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - lendingPrecision: JSBI.BigInt('1'), - precision: JSBI.BigInt('18'), - feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - feeIndex: 0, - decimals: [JSBI.BigInt('18'), JSBI.BigInt('6')], - peggedTo: '$', - pegComesAfter: false, - displayDecimals: 0, - gaugeAddress: '0x0A125D473cd3b1968e728DDF7d424c928C09222A', - additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], - additionalRewardRate: [weeklyEmissionToSeconds(2520)], // ['7388020830000000'], //['11080000000000000'], // ['2190848200000000'], - displayChain: Chain.Polygon, - coin: Coins.USD, - }, - { - name: 'USDC (Optics V1)', - tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0x2A3684e9Dc20B857375EA04235F2F7edBe818FA7'], - tokens: [ - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', - decimals: 18, - symbol: 'cUSD', - name: 'Celo Dollar', - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', - }, - [] - ), - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x2A3684e9Dc20B857375EA04235F2F7edBe818FA7', - decimals: 6, - symbol: 'cUSDCxV1', - name: 'US Dollar Coin (Optics Bridge)', - logoURI: 'https://bit.ly/3CwGimW', - }, - [] - ), - ], - address: '0xA5037661989789d0310aC2B796fa78F1B01F195D', - lpToken: new Token( - ChainId.MAINNET, - '0xd7Bf6946b740930c60131044bD2F08787e1DdBd4', - 18, - 'MobLP', - 'Mobius cUSD/cUSDC(O) LP' - ), - swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), - rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - lendingPrecision: JSBI.BigInt('1'), - precision: JSBI.BigInt('18'), - feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - feeIndex: 0, - decimals: [JSBI.BigInt('18'), JSBI.BigInt('6')], - peggedTo: '$', - pegComesAfter: false, - displayDecimals: 0, - gaugeAddress: '0xdAA2ab880b7f3D5697e6F85e63c28b9120AA9E07', - additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], - additionalRewardRate: ['0'], // ['7302827380000000'] - displayChain: Chain.Ethereum, - coin: Coins.USD, - isKilled: true, - }, - { - name: 'aaUSDC (Allbridge)', - tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0xb70e0a782b058BFdb0d109a3599BEc1f19328E36'], - tokens: [ - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', - decimals: 18, - symbol: 'cUSD', - name: 'Celo Dollar', - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', - }, - [] - ), - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0xb70e0a782b058BFdb0d109a3599BEc1f19328E36', - decimals: 18, - symbol: 'aaUSDC', - name: 'US Dollar Coin (Avalanche Allbridge)', - logoURI: 'https://bit.ly/3CwGimW', - }, - [] - ), - ], - address: '0x0986B42F5f9C42FeEef66fC23eba9ea1164C916D', - lpToken: new Token( - ChainId.MAINNET, - '0x730e677f39C4Ca96012c394B9Da09A025E922F81', - 18, - 'MobLP', - 'Mobius cUSD/aaUSDC LP' - ), - swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), - rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - lendingPrecision: JSBI.BigInt('1'), - precision: JSBI.BigInt('18'), - feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - feeIndex: 0, - decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], - peggedTo: '$', - pegComesAfter: false, - displayDecimals: 0, - gaugeAddress: '0xF2ae5c2D2D2eD13dd324C0942163054fc4A3D4d9', - displayChain: Chain.Avax, - coin: Coins.USD, - }, - { - name: 'Poof cUSD V2', - warningType: WarningType.POOF, - tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0xEadf4A7168A82D30Ba0619e64d5BCf5B30B45226'], - tokens: [ - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', - decimals: 18, - symbol: 'cUSD', - name: 'Celo Dollar', - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', - }, - [] - ), - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0xEadf4A7168A82D30Ba0619e64d5BCf5B30B45226', - decimals: 18, - symbol: 'pUSD', - name: 'Poof USD V2', - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pUSD.png', - }, - [] - ), - ], - address: '0xa2F0E57d4cEAcF025E81C76f28b9Ad6E9Fbe8735', - lpToken: new Token( - ChainId.MAINNET, - '0x07e137E5605E15C93f22545868Fa70CECfCbbFFE', - 18, - 'MobLP', - 'Mobius pUSD V2 LP' - ), - swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), - rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - lendingPrecision: JSBI.BigInt('1'), - precision: JSBI.BigInt('18'), - feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - feeIndex: 0, - decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], - peggedTo: '$', - pegComesAfter: false, - displayDecimals: 0, - gaugeAddress: '0xE7195E651Cc47853f0054d85c8ADFc79D532929f', - additionalRewards: ['0x00400FcbF0816bebB94654259de7273f4A05c762'], - additionalRewardRate: ['6283068780000000'], - displayChain: Chain.Celo, - coin: Coins.USD, - }, - { - name: 'Poof CELO V2', - warningType: WarningType.POOF, - tokenAddresses: ['0x471EcE3750Da237f93B8E339c536989b8978a438', '0x301a61D01A63c8D670c2B8a43f37d12eF181F997'], - tokens: [ - new WrappedTokenInfo( - { - address: '0x471EcE3750Da237f93B8E339c536989b8978a438', - name: 'Celo', - symbol: 'CELO', - chainId: 42220, - decimals: 18, - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_CELO.png', - }, - [] - ), - new WrappedTokenInfo( - { - address: '0x301a61D01A63c8D670c2B8a43f37d12eF181F997', - name: 'Poof Celo V2', - symbol: 'pCELO', - chainId: 42220, - decimals: 18, - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pCELO.png', - }, - [] - ), - ], - address: '0xFc9e2C63370D8deb3521922a7B2b60f4Cff7e75a', - lpToken: new Token( - ChainId.MAINNET, - '0xAfFD8d6b5e5A0e25034DD3D075532F9CE01C305c', - 18, - 'MobLP', - 'Mobius pCelo V2 LP' - ), - swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), - rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - lendingPrecision: JSBI.BigInt('1'), - precision: JSBI.BigInt('18'), - feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - feeIndex: 0, - decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], - peggedTo: 'Celo', - pegComesAfter: true, - displayDecimals: 0, - gaugeAddress: '0xD0d57a6689188F854F996BEAE0Cb1949FDB5FF86', - additionalRewards: ['0x00400FcbF0816bebB94654259de7273f4A05c762'], - additionalRewardRate: ['6283068780000000'], - displayChain: Chain.Celo, - coin: Coins.Celo, - }, - { - name: 'Poof cEUR V2', - warningType: WarningType.POOF, - tokenAddresses: ['0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73', '0xD8761DD6c7cB54febD33adD699F5E4440b62E01B'], - tokens: [ - new WrappedTokenInfo( - { - address: '0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73', - name: 'Celo Euro', - symbol: 'cEUR', - chainId: 42220, - decimals: 18, - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cEUR.png', - }, - [] - ), - new WrappedTokenInfo( - { - address: '0xD8761DD6c7cB54febD33adD699F5E4440b62E01B', - name: 'Poof EUR', - symbol: 'pEUR', - chainId: 42220, - decimals: 18, - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pEUR.png', - }, - [] - ), - ], - address: '0x23C95678862a229fAC088bd9705622d78130bC3e', - lpToken: new Token( - ChainId.MAINNET, - '0xec8e37876Fd9De919B58788B87A078e546149F87', - 18, - 'MobLP', - 'Mobius pEUR V2 LP' - ), - swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), - rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - lendingPrecision: JSBI.BigInt('1'), - precision: JSBI.BigInt('18'), - feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - feeIndex: 0, - decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], - peggedTo: '€', - pegComesAfter: false, - displayDecimals: 0, - gaugeAddress: '0xCAEd243de23264Bdd8297c6eECcF320846eee18A', - additionalRewards: ['0x00400FcbF0816bebB94654259de7273f4A05c762'], - additionalRewardRate: ['6283068780000000'], - // additionalRewards: [''], - // additionalRewardRate: ['730282730000000'], - displayChain: Chain.Celo, - coin: Coins.Eur, - }, - { - name: 'Poof cUSD V1', - warningType: WarningType.POOF, - tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0xB4aa2986622249B1F45eb93F28Cfca2b2606d809'], - tokens: [ - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', - decimals: 18, - symbol: 'cUSD', - name: 'Celo Dollar', - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', - }, - [] - ), - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0xB4aa2986622249B1F45eb93F28Cfca2b2606d809', - decimals: 18, - symbol: 'pUSDxV1', - name: 'Poof USD V1', - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pUSD.png', - }, - [] - ), - ], - address: '0x02Db089fb09Fda92e05e92aFcd41D9AAfE9C7C7C', - lpToken: new Token( - ChainId.MAINNET, - '0x18d71b8664E69D6Dd61C79247dBf12bFAaf66C10', - 18, - 'MobLP', - 'Mobius pUSD V1 LP' - ), - swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), - rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - lendingPrecision: JSBI.BigInt('1'), - precision: JSBI.BigInt('18'), - feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - feeIndex: 0, - decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], - peggedTo: '$', - pegComesAfter: false, - displayDecimals: 0, - gaugeAddress: '0x2459BDb59a3BF6Ab6C412Ac0b220e7CDA1D4ea26', - additionalRewards: ['0x00400FcbF0816bebB94654259de7273f4A05c762', '0x17700282592D6917F6A73D0bF8AcCf4D578c131e'], - additionalRewardRate: ['0', '0'], - displayChain: Chain.Celo, - coin: Coins.USD, - isKilled: true, - }, - { - name: 'asUSDC (AllBridge)', - tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0xCD7D7Ff64746C1909E44Db8e95331F9316478817'], - tokens: [ - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', - decimals: 18, - symbol: 'cUSD', - name: 'Celo Dollar', - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', - }, - [] - ), - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0xCD7D7Ff64746C1909E44Db8e95331F9316478817', - decimals: 18, - symbol: 'asUSDC', - name: 'US Dollar Coin (Solana AllBridge)', - logoURI: 'https://bit.ly/3CwGimW', - }, - [] - ), - ], - address: '0x63C1914bf00A9b395A2bF89aaDa55A5615A3656e', - lpToken: new Token( - ChainId.MAINNET, - '0xAFEe90ab6A2D3B265262f94F6e437E7f6d94e26E', - 18, - 'MobLP', - 'Mobius cUSD/asUSDC LP' - ), - swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), - rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - lendingPrecision: JSBI.BigInt('1'), - precision: JSBI.BigInt('18'), - feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - feeIndex: 0, - decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], - peggedTo: '$', - pegComesAfter: false, - displayDecimals: 0, - gaugeAddress: '0x27D9Bfa5F864862BeDC23cFab7e00b6b94488CC6', - additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], - additionalRewardRate: [weeklyEmissionToSeconds(3780)], - displayChain: Chain.Solana, - coin: Coins.USD, - }, - { - name: 'pUSDC (Optics V1)', - tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0xcC82628f6A8dEFA1e2B0aD7ed448bef3647F7941'], - tokens: [ - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', - decimals: 18, - symbol: 'cUSD', - name: 'Celo Dollar', - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', - }, - [] - ), - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0xcC82628f6A8dEFA1e2B0aD7ed448bef3647F7941', - decimals: 6, - symbol: 'pUSDCxV1', - name: 'USD Coin (PoS Optics)', - logoURI: 'https://bit.ly/3CwGimW', - }, - [] - ), - ], - address: '0x2080AAa167e2225e1FC9923250bA60E19a180Fb2', - lpToken: new Token( - ChainId.MAINNET, - '0xf5b454cF47Caca418D95930AA03975Ee4bf409bc', - 18, - 'MobLP', - 'Mobius cUSD/pUSDC LP' - ), - swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), - rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - lendingPrecision: JSBI.BigInt('1'), - precision: JSBI.BigInt('18'), - feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - feeIndex: 0, - decimals: [JSBI.BigInt('18'), JSBI.BigInt('6')], - peggedTo: '$', - pegComesAfter: false, - displayDecimals: 0, - gaugeAddress: '0x52517feb1Fc6141d5CF6718111C7Cc0FD764fA5d', - additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], - additionalRewardRate: ['0'], // ['2190848200000000'], - displayChain: Chain.Polygon, - coin: Coins.USD, - isKilled: true, - }, - { - name: 'wBTC (Optics V1)', - tokenAddresses: ['0xD629eb00dEced2a080B7EC630eF6aC117e614f1b', '0xBe50a3013A1c94768A1ABb78c3cB79AB28fc1aCE'], - tokens: [ - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0xD629eb00dEced2a080B7EC630eF6aC117e614f1b', - decimals: 18, - symbol: 'cBTC', - name: 'Wrapped Bitcoin', - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cBTC.png', - }, - [] - ), - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0xBe50a3013A1c94768A1ABb78c3cB79AB28fc1aCE', - decimals: 8, - symbol: 'wBTCxV1', - name: 'Wrapped Bitcoin (Optics Bridge)', - logoURI: 'https://etherscan.io/token/images/wbtc_28.png?v=1', - }, - [] - ), - ], - address: '0x19260b9b573569dDB105780176547875fE9fedA3', - lpToken: new Token( - ChainId.MAINNET, - '0x8cD0E2F11ed2E896a8307280dEEEE15B27e46BbE', - 18, - 'MobLP', - 'Mobius cBTC/wBTC LP' - ), - swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), - rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - lendingPrecision: JSBI.BigInt('1'), - precision: JSBI.BigInt('18'), - feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - feeIndex: 0, - decimals: [JSBI.BigInt('18'), JSBI.BigInt('8')], - peggedTo: '₿', - pegComesAfter: true, - displayDecimals: 2, - gaugeAddress: '0x1A8938a37093d34581B21bAd2AE7DC1c19150C05', - displayChain: Chain.Ethereum, - coin: Coins.Bitcoin, - isKilled: true, - }, - { - name: 'WETH (Optics V1)', - tokenAddresses: ['0x2DEf4285787d58a2f811AF24755A8150622f4361', '0xE919F65739c26a42616b7b8eedC6b5524d1e3aC4'], - tokens: [ - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x2DEf4285787d58a2f811AF24755A8150622f4361', - decimals: 18, - symbol: 'cETH', - name: 'Wrapped Ethereum', - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cETH.svg', - }, - [] - ), - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0xE919F65739c26a42616b7b8eedC6b5524d1e3aC4', - decimals: 18, - symbol: 'wETHxV1', - name: 'Wrapped Ether (Optics Bridge)', - logoURI: 'https://etherscan.io/token/images/weth_28.png', - }, - [] - ), - ], - address: '0xE0F2cc70E52f05eDb383313393d88Df2937DA55a', - lpToken: new Token( - ChainId.MAINNET, - '0x846b784Ab5302155542c1B3952B54305F220fd84', - 18, - 'MobLP', - 'Mobius cETH/wETH LP' - ), - swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), - rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - lendingPrecision: JSBI.BigInt('1'), - precision: JSBI.BigInt('18'), - feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - feeIndex: 0, - decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], - peggedTo: 'Ξ', - pegComesAfter: true, - displayDecimals: 2, - gaugeAddress: '0xD38e76E17E66b562B61c149Ca0EE53CEa1145733', - displayChain: Chain.Ethereum, - coin: Coins.Ether, - isKilled: true, - }, - { - name: 'USDT (Moss)', - tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0xcFFfE0c89a779c09Df3DF5624f54cDf7EF5fDd5D'], - tokens: [ - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', - decimals: 18, - symbol: 'cUSD', - name: 'Celo Dollar', - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', - }, - [] - ), - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0xcFFfE0c89a779c09Df3DF5624f54cDf7EF5fDd5D', - decimals: 18, - symbol: 'cUSDTm', - name: 'Tether (Moss Bridge)', - logoURI: 'https://bit.ly/3AMrCyD', - }, - [] - ), - ], - address: '0xdBF27fD2a702Cc02ac7aCF0aea376db780D53247', - lpToken: new Token( - ChainId.MAINNET, - '0xC7a4c6EF4A16Dc24634Cc2A951bA5Fec4398f7e0', - 18, - 'MobLP', - 'Mobius cUSD/cUSDT LP' - ), - swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), - rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - lendingPrecision: JSBI.BigInt('1'), - precision: JSBI.BigInt('18'), - feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - feeIndex: 0, - decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], - peggedTo: '$', - pegComesAfter: false, - displayDecimals: 0, - gaugeAddress: '0xe2d6095685248F38Ae9fef1b360D772b78Ea19D1', - displayChain: Chain.Ethereum, - coin: Coins.USD, - }, - { - name: 'USDC (Moss)', - tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0x93DB49bE12B864019dA9Cb147ba75cDC0506190e'], - tokens: [ - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', - decimals: 18, - symbol: 'cUSD', - name: 'Celo Dollar', - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', - }, - [] - ), - new WrappedTokenInfo( - { - chainId: ChainId.MAINNET, - address: '0x93DB49bE12B864019dA9Cb147ba75cDC0506190e', - decimals: 18, - symbol: 'cUSDCm', - name: 'US Dollar Coin (Moss Bridge)', - logoURI: 'https://bit.ly/3CwGimW', - }, - [] - ), - ], - address: '0x0ff04189Ef135b6541E56f7C638489De92E9c778', - lpToken: new Token( - ChainId.MAINNET, - '0x635aec36c4b61bac5eB1C3EEe191147d006F8a21', - 18, - 'MobLP', - 'Mobius cUSD/cUSDC LP' - ), - swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), - rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - lendingPrecision: JSBI.BigInt('1'), - precision: JSBI.BigInt('18'), - feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - feeIndex: 0, - decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], - peggedTo: '$', - pegComesAfter: false, - displayDecimals: 0, - gaugeAddress: '0xd1B3C05FE24bda6F52e704daf1ACBa8c440d8573', - additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], - additionalRewardRate: ['0'], //['730282730000000'], - displayChain: Chain.Ethereum, - coin: Coins.USD, - }, - { - name: 'Poof CELO V1', - warningType: WarningType.POOF, - tokenAddresses: ['0x471EcE3750Da237f93B8E339c536989b8978a438', '0xE74AbF23E1Fdf7ACbec2F3a30a772eF77f1601E1'], - tokens: [ - new WrappedTokenInfo( - { - address: '0x471EcE3750Da237f93B8E339c536989b8978a438', - name: 'Celo', - symbol: 'CELO', - chainId: 42220, - decimals: 18, - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_CELO.png', - }, - [] - ), - new WrappedTokenInfo( - { - address: '0xE74AbF23E1Fdf7ACbec2F3a30a772eF77f1601E1', - name: 'Poof Celo V1', - symbol: 'pCELOxV1', - chainId: 42220, - decimals: 18, - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pCELO.png', - }, - [] - ), - ], - address: '0x413FfCc28e6cDDE7e93625Ef4742810fE9738578', - lpToken: new Token( - ChainId.MAINNET, - '0x4D6B17828d0173668e8Eb730106444556a98c0F9', - 18, - 'MobLP', - 'Mobius pCelo V1 LP' - ), - swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), - rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - lendingPrecision: JSBI.BigInt('1'), - precision: JSBI.BigInt('18'), - feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - feeIndex: 0, - decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], - peggedTo: 'Celo', - pegComesAfter: true, - displayDecimals: 0, - gaugeAddress: '0x5489b2F0A1992b889F47601D71E068Fd15c63f26', - additionalRewards: ['0x00400FcbF0816bebB94654259de7273f4A05c762', '0x17700282592D6917F6A73D0bF8AcCf4D578c131e'], - additionalRewardRate: ['0', '0'], - // additionalRewards: [''], - // additionalRewardRate: ['730282730000000'], - displayChain: Chain.Celo, - coin: Coins.Celo, - isKilled: true, - }, - { - name: 'Poof cEUR V1', - warningType: WarningType.POOF, - tokenAddresses: ['0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73', '0x56072D4832642dB29225dA12d6Fd1290E4744682'], - tokens: [ - new WrappedTokenInfo( - { - address: '0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73', - name: 'Celo Euro', - symbol: 'cEUR', - chainId: 42220, - decimals: 18, - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cEUR.png', - }, - [] - ), - new WrappedTokenInfo( - { - address: '0x56072D4832642dB29225dA12d6Fd1290E4744682', - name: 'Poof EUR V1', - symbol: 'pEURxV1', - chainId: 42220, - decimals: 18, - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pEUR.png', - }, - [] - ), - ], - address: '0x382Ed834c6b7dBD10E4798B08889eaEd1455E820', - lpToken: new Token( - ChainId.MAINNET, - '0x2642Ab16Bfb7A8b36EE42c9CbA2289C4Ca9F33b9', - 18, - 'MobLP', - 'Mobius pEUR V1 LP' - ), - swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), - rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - lendingPrecision: JSBI.BigInt('1'), - precision: JSBI.BigInt('18'), - feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - feeIndex: 0, - decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], - peggedTo: '€', - pegComesAfter: false, - displayDecimals: 0, - gaugeAddress: '0xCF34F4ec5DC9E09428A4f4a45475f6277694166c', - additionalRewards: ['0x00400FcbF0816bebB94654259de7273f4A05c762', '0x17700282592D6917F6A73D0bF8AcCf4D578c131e'], - additionalRewardRate: ['0', '0'], - // additionalRewards: [''], - // additionalRewardRate: ['730282730000000'], - displayChain: Chain.Celo, - coin: Coins.Eur, - isKilled: true, - }, - // { - // name: 'Poof cUSD V1 [DISABLED]', - // warningType: WarningType.POOF, - // tokenAddresses: ['0xB4aa2986622249B1F45eb93F28Cfca2b2606d809'], - // tokens: [ - // new WrappedTokenInfo( - // { - // chainId: ChainId.MAINNET, - // address: '0xB4aa2986622249B1F45eb93F28Cfca2b2606d809', - // decimals: 18, - // symbol: 'pUSDxV1', - // name: 'Poof USD V1', - // logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pUSD.png', - // }, - // [] - // ), - // new WrappedTokenInfo( - // { - // chainId: ChainId.MAINNET, - // address: '0xd7Bf6946b740930c60131044bD2F08787e1DdBd4', - // decimals: 18, - // symbol: 'Mob LP', - // name: 'Mobius USDC LP', - // logoURI: 'https://bit.ly/3CwGimW', - // }, - // [] - // ), - // ], - // address: '0x81B6a3d9f725AB5d706d9e552b128bC5bB0B58a1', - // lpToken: new Token( - // ChainId.MAINNET, - // '0x57f008172cF89b972db3db7dD032e66BE4AF1A8c', - // 18, - // 'MobLP', - // 'Mobius pUSD Meta LP' - // ), - // swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), - // rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], - // lendingPrecision: JSBI.BigInt('1'), - // precision: JSBI.BigInt('18'), - // feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), - // precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], - // feeIndex: 0, - // decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], - // peggedTo: '$', - // pegComesAfter: false, - // displayDecimals: 0, - // gaugeAddress: '0x1250D6dd3B51D20c14a8ECb10CC2dd713967767e', - // metaPool: 'USDC (Optics)', - // displayChain: Chain.Celo, - // coin: Coins.USD, - // disabled: true, - // isKilled: true, - // }, - ], - [ChainId.ALFAJORES]: [], - [ChainId.BAKLAVA]: [], -} - -//todo: replace Mainnet and Baklava Pool Addresses -type AddressMap = { [K in ChainId]: string } - -export const LP_VEST_ADDRESSES: AddressMap = { - [ChainId.MAINNET]: '0x74Fc71eF736feeaCfd58aeb2543c5fe4d33aDc14', - [ChainId.ALFAJORES]: '0x9ff6d45F5900D7aCBdCb6d79fFFf22C9F63dF040', - [ChainId.BAKLAVA]: '', -} - -export const FOUNDER_VEST_ADDRESSES: AddressMap = { - [ChainId.MAINNET]: '0x34deFd314fa23821a87FCbF5393311Bc5B7608C1', - [ChainId.ALFAJORES]: '0x9ff6d45F5900D7aCBdCb6d79fFFf22C9F63dF040', - [ChainId.BAKLAVA]: '', -} - -export const INVESTOR_VEST_ADDRESSES: AddressMap = { - [ChainId.MAINNET]: '0x5498248EaB20ff314bC465268920B48eed4Cdb7C', - [ChainId.ALFAJORES]: '0x9ff6d45F5900D7aCBdCb6d79fFFf22C9F63dF040', - [ChainId.BAKLAVA]: '', -} - -export const ADVISOR_VEST_ADDRESSES: AddressMap = { - [ChainId.MAINNET]: '0x54Bf52862E1Fdf0D43D9B19Abb5ec72acA0a25A6', - [ChainId.ALFAJORES]: '0x9ff6d45F5900D7aCBdCb6d79fFFf22C9F63dF040', - [ChainId.BAKLAVA]: '', -} - -export const VestingAddresses: { [type in VestType]: AddressMap } = { - [VestType.FOUNDER]: FOUNDER_VEST_ADDRESSES, - [VestType.ADVISOR]: ADVISOR_VEST_ADDRESSES, - [VestType.INVESTOR]: INVESTOR_VEST_ADDRESSES, - [VestType.LP]: LP_VEST_ADDRESSES, -} +// export const STATIC_POOL_INFO: { [K in ChainId]: StableSwapConstants[] } = { +// [ChainId.MAINNET]: [ +// { +// name: 'UST (Allbridge)', +// tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0xEd193C4E69F591E42398eF54DEa65aa1bb02835c'], +// tokens: [ +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', +// decimals: 18, +// symbol: 'cUSD', +// name: 'Celo Dollar', +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', +// }, +// [] +// ), +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0xEd193C4E69F591E42398eF54DEa65aa1bb02835c', +// decimals: 18, +// symbol: 'UST', +// name: 'Allbridge UST', +// logoURI: 'https://raw.githubusercontent.com/kyscott18/default-token-list/master/assets/asset_UST.png', +// }, +// [] +// ), +// ], +// address: '0x9F4AdBD0af281C69a582eB2E6fa2A594D4204CAe', +// lpToken: new Token( +// ChainId.MAINNET, +// '0x9438e7281D7E3e99A9dD21e0EAd9c6a254e17ab2', +// 18, +// 'MobLP', +// 'Mobius cUSD/aUST LP' +// ), +// swapFee: JSBI.multiply(JSBI.BigInt('2'), JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7'))), +// rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// lendingPrecision: JSBI.BigInt('1'), +// precision: JSBI.BigInt('18'), +// feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// feeIndex: 0, +// decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], +// peggedTo: '$', +// pegComesAfter: false, +// displayDecimals: 0, +// gaugeAddress: '0x107F94409746E8c8E6eFF139A100D17D9ca7FdfE', +// additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], +// additionalRewardRate: [weeklyEmissionToSeconds(2268)], // ['14776041660000000'], //['18468900000000000'], // ['7302827380000000'] +// displayChain: Chain.Terra, +// coin: Coins.USD, +// }, +// { +// name: 'USDC (Optics V2)', +// tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0xef4229c8c3250C675F21BCefa42f58EfbfF6002a'], +// tokens: [ +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', +// decimals: 18, +// symbol: 'cUSD', +// name: 'Celo Dollar', +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', +// }, +// [] +// ), +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0xef4229c8c3250C675F21BCefa42f58EfbfF6002a', +// decimals: 6, +// symbol: 'cUSDC', +// name: 'US Dollar Coin (Optics Bridge)', +// logoURI: 'https://bit.ly/3CwGimW', +// }, +// [] +// ), +// ], +// address: '0x9906589Ea8fd27504974b7e8201DF5bBdE986b03', +// lpToken: new Token( +// ChainId.MAINNET, +// '0x39b6F09ef97dB406ab78D869471adb2384C494E3', +// 18, +// 'MobLP', +// 'Mobius cUSD/cUSDC LP' +// ), +// swapFee: JSBI.multiply(JSBI.BigInt('2'), JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7'))), +// rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// lendingPrecision: JSBI.BigInt('1'), +// precision: JSBI.BigInt('18'), +// feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// feeIndex: 0, +// decimals: [JSBI.BigInt('18'), JSBI.BigInt('6')], +// peggedTo: '$', +// pegComesAfter: false, +// displayDecimals: 0, +// gaugeAddress: '0xc96AeeaFF32129da934149F6134Aa7bf291a754E', +// additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], +// additionalRewardRate: [weeklyEmissionToSeconds(11088)], // ['29552083330000000'], // ['36940104160000000'], // ['7302827380000000'] +// displayChain: Chain.Ethereum, +// coin: Coins.USD, +// }, +// { +// name: 'DAI (Optics V2)', +// tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0x90Ca507a5D4458a4C6C6249d186b6dCb02a5BCCd'], +// tokens: [ +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', +// decimals: 18, +// symbol: 'cUSD', +// name: 'Celo Dollar', +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', +// }, +// [] +// ), +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0x90Ca507a5D4458a4C6C6249d186b6dCb02a5BCCd', +// decimals: 18, +// symbol: 'DAI', +// name: 'Optics DAI', +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_DAI.png', +// }, +// [] +// ), +// ], +// address: '0xF3f65dFe0c8c8f2986da0FEc159ABE6fd4E700B4', +// lpToken: new Token( +// ChainId.MAINNET, +// '0x274DD2dF039f1f6131419C82173D97770e6af6B7', +// 18, +// 'MobLP', +// 'Mobius cUSD/cDAI LP' +// ), +// swapFee: JSBI.multiply(JSBI.BigInt('2'), JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7'))), +// rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// lendingPrecision: JSBI.BigInt('1'), +// precision: JSBI.BigInt('18'), +// feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// feeIndex: 0, +// decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], +// peggedTo: '$', +// pegComesAfter: false, +// displayDecimals: 0, +// gaugeAddress: '0xE1f9D952EecC07cfEFa69df9fBB0cEF260957119', +// additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], +// additionalRewardRate: [weeklyEmissionToSeconds(3780)], // ['14776041660000000'], //['18468900000000000'], // ['7302827380000000'] +// displayChain: Chain.Ethereum, +// coin: Coins.USD, +// }, +// { +// name: 'WETH (Optics V2)', +// tokenAddresses: ['0x2DEf4285787d58a2f811AF24755A8150622f4361', '0x122013fd7dF1C6F636a5bb8f03108E876548b455'], +// tokens: [ +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0x2DEf4285787d58a2f811AF24755A8150622f4361', +// decimals: 18, +// symbol: 'cETH', +// name: 'Wrapped Ethereum', +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cETH.svg', +// }, +// [] +// ), +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0x122013fd7dF1C6F636a5bb8f03108E876548b455', +// decimals: 18, +// symbol: 'wETH', +// name: 'Wrapped Ether (Optics Bridge)', +// logoURI: 'https://etherscan.io/token/images/weth_28.png', +// }, +// [] +// ), +// ], +// address: '0x74ef28D635c6C5800DD3Cd62d4c4f8752DaACB09', +// lpToken: new Token( +// ChainId.MAINNET, +// '0x4fF08e2a4E7114af4B575AeF9250144f95790982', +// 18, +// 'MobLP', +// 'Mobius cETH/wETH LP' +// ), +// swapFee: JSBI.multiply(JSBI.BigInt('2'), JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7'))), +// rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// lendingPrecision: JSBI.BigInt('1'), +// precision: JSBI.BigInt('18'), +// feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// feeIndex: 0, +// decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], +// peggedTo: 'Ξ', +// pegComesAfter: true, +// displayDecimals: 2, +// gaugeAddress: '0x487c30CB18AA9Ced435911E2B414e0e85D7E52bB', +// displayChain: Chain.Ethereum, +// coin: Coins.Ether, +// additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], +// additionalRewardRate: ['0'], // ['3694010416000000'], // ['7302827380000000'] +// }, +// { +// name: 'wBTC (Optics V2)', +// tokenAddresses: ['0xD629eb00dEced2a080B7EC630eF6aC117e614f1b', '0xBAAB46E28388d2779e6E31Fd00cF0e5Ad95E327B'], +// tokens: [ +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0xD629eb00dEced2a080B7EC630eF6aC117e614f1b', +// decimals: 18, +// symbol: 'cBTC', +// name: 'Wrapped Bitcoin', +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cBTC.png', +// }, +// [] +// ), +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0xBAAB46E28388d2779e6E31Fd00cF0e5Ad95E327B', +// decimals: 8, +// symbol: 'wBTC', +// name: 'Wrapped Bitcoin (Optics Bridge)', +// logoURI: 'https://etherscan.io/token/images/wbtc_28.png?v=1', +// }, +// [] +// ), +// ], +// address: '0xaEFc4e8cF655a182E8346B24c8AbcE45616eE0d2', +// lpToken: new Token( +// ChainId.MAINNET, +// '0x20d7274C5aF4f9DE6e8C93025e44aF3979d9Ab2b', +// 18, +// 'MobLP', +// 'Mobius cBTC/wBTC LP' +// ), +// swapFee: JSBI.multiply(JSBI.BigInt('2'), JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7'))), +// rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// lendingPrecision: JSBI.BigInt('1'), +// precision: JSBI.BigInt('18'), +// feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// feeIndex: 0, +// decimals: [JSBI.BigInt('18'), JSBI.BigInt('8')], +// peggedTo: '₿', +// pegComesAfter: true, +// displayDecimals: 2, +// gaugeAddress: '0x127b524c74C2162Ee4BB2e42d8d2eB9050C0293E', +// displayChain: Chain.Ethereum, +// coin: Coins.Bitcoin, +// additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], +// additionalRewardRate: ['0'], // ['3694010416000000'], // ['7302827380000000'] +// }, +// { +// name: 'pUSDC (Optics V2)', +// tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0x1bfc26cE035c368503fAE319Cc2596716428ca44'], +// tokens: [ +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', +// decimals: 18, +// symbol: 'cUSD', +// name: 'Celo Dollar', +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', +// }, +// [] +// ), +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0x1bfc26cE035c368503fAE319Cc2596716428ca44', +// decimals: 6, +// symbol: 'pUSDC', +// name: 'USD Coin (PoS Optics)', +// logoURI: 'https://bit.ly/3CwGimW', +// }, +// [] +// ), +// ], +// address: '0xcCe0d62Ce14FB3e4363Eb92Db37Ff3630836c252', +// lpToken: new Token( +// ChainId.MAINNET, +// '0x68b239b415970dD7a5234A9701cbB5BfaB544C7C', +// 18, +// 'MobLP', +// 'Mobius cUSD/pUSDC LP' +// ), +// swapFee: JSBI.multiply(JSBI.BigInt('2'), JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7'))), +// rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// lendingPrecision: JSBI.BigInt('1'), +// precision: JSBI.BigInt('18'), +// feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// feeIndex: 0, +// decimals: [JSBI.BigInt('18'), JSBI.BigInt('6')], +// peggedTo: '$', +// pegComesAfter: false, +// displayDecimals: 0, +// gaugeAddress: '0x0A125D473cd3b1968e728DDF7d424c928C09222A', +// additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], +// additionalRewardRate: [weeklyEmissionToSeconds(2520)], // ['7388020830000000'], //['11080000000000000'], // ['2190848200000000'], +// displayChain: Chain.Polygon, +// coin: Coins.USD, +// }, +// { +// name: 'USDC (Optics V1)', +// tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0x2A3684e9Dc20B857375EA04235F2F7edBe818FA7'], +// tokens: [ +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', +// decimals: 18, +// symbol: 'cUSD', +// name: 'Celo Dollar', +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', +// }, +// [] +// ), +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0x2A3684e9Dc20B857375EA04235F2F7edBe818FA7', +// decimals: 6, +// symbol: 'cUSDCxV1', +// name: 'US Dollar Coin (Optics Bridge)', +// logoURI: 'https://bit.ly/3CwGimW', +// }, +// [] +// ), +// ], +// address: '0xA5037661989789d0310aC2B796fa78F1B01F195D', +// lpToken: new Token( +// ChainId.MAINNET, +// '0xd7Bf6946b740930c60131044bD2F08787e1DdBd4', +// 18, +// 'MobLP', +// 'Mobius cUSD/cUSDC(O) LP' +// ), +// swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), +// rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// lendingPrecision: JSBI.BigInt('1'), +// precision: JSBI.BigInt('18'), +// feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// feeIndex: 0, +// decimals: [JSBI.BigInt('18'), JSBI.BigInt('6')], +// peggedTo: '$', +// pegComesAfter: false, +// displayDecimals: 0, +// gaugeAddress: '0xdAA2ab880b7f3D5697e6F85e63c28b9120AA9E07', +// additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], +// additionalRewardRate: ['0'], // ['7302827380000000'] +// displayChain: Chain.Ethereum, +// coin: Coins.USD, +// isKilled: true, +// }, +// { +// name: 'aaUSDC (Allbridge)', +// tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0xb70e0a782b058BFdb0d109a3599BEc1f19328E36'], +// tokens: [ +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', +// decimals: 18, +// symbol: 'cUSD', +// name: 'Celo Dollar', +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', +// }, +// [] +// ), +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0xb70e0a782b058BFdb0d109a3599BEc1f19328E36', +// decimals: 18, +// symbol: 'aaUSDC', +// name: 'US Dollar Coin (Avalanche Allbridge)', +// logoURI: 'https://bit.ly/3CwGimW', +// }, +// [] +// ), +// ], +// address: '0x0986B42F5f9C42FeEef66fC23eba9ea1164C916D', +// lpToken: new Token( +// ChainId.MAINNET, +// '0x730e677f39C4Ca96012c394B9Da09A025E922F81', +// 18, +// 'MobLP', +// 'Mobius cUSD/aaUSDC LP' +// ), +// swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), +// rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// lendingPrecision: JSBI.BigInt('1'), +// precision: JSBI.BigInt('18'), +// feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// feeIndex: 0, +// decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], +// peggedTo: '$', +// pegComesAfter: false, +// displayDecimals: 0, +// gaugeAddress: '0xF2ae5c2D2D2eD13dd324C0942163054fc4A3D4d9', +// displayChain: Chain.Avax, +// coin: Coins.USD, +// }, +// { +// name: 'Poof cUSD V2', +// warningType: WarningType.POOF, +// tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0xEadf4A7168A82D30Ba0619e64d5BCf5B30B45226'], +// tokens: [ +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', +// decimals: 18, +// symbol: 'cUSD', +// name: 'Celo Dollar', +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', +// }, +// [] +// ), +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0xEadf4A7168A82D30Ba0619e64d5BCf5B30B45226', +// decimals: 18, +// symbol: 'pUSD', +// name: 'Poof USD V2', +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pUSD.png', +// }, +// [] +// ), +// ], +// address: '0xa2F0E57d4cEAcF025E81C76f28b9Ad6E9Fbe8735', +// lpToken: new Token( +// ChainId.MAINNET, +// '0x07e137E5605E15C93f22545868Fa70CECfCbbFFE', +// 18, +// 'MobLP', +// 'Mobius pUSD V2 LP' +// ), +// swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), +// rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// lendingPrecision: JSBI.BigInt('1'), +// precision: JSBI.BigInt('18'), +// feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// feeIndex: 0, +// decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], +// peggedTo: '$', +// pegComesAfter: false, +// displayDecimals: 0, +// gaugeAddress: '0xE7195E651Cc47853f0054d85c8ADFc79D532929f', +// additionalRewards: ['0x00400FcbF0816bebB94654259de7273f4A05c762'], +// additionalRewardRate: ['6283068780000000'], +// displayChain: Chain.Celo, +// coin: Coins.USD, +// }, +// { +// name: 'Poof CELO V2', +// warningType: WarningType.POOF, +// tokenAddresses: ['0x471EcE3750Da237f93B8E339c536989b8978a438', '0x301a61D01A63c8D670c2B8a43f37d12eF181F997'], +// tokens: [ +// new WrappedTokenInfo( +// { +// address: '0x471EcE3750Da237f93B8E339c536989b8978a438', +// name: 'Celo', +// symbol: 'CELO', +// chainId: 42220, +// decimals: 18, +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_CELO.png', +// }, +// [] +// ), +// new WrappedTokenInfo( +// { +// address: '0x301a61D01A63c8D670c2B8a43f37d12eF181F997', +// name: 'Poof Celo V2', +// symbol: 'pCELO', +// chainId: 42220, +// decimals: 18, +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pCELO.png', +// }, +// [] +// ), +// ], +// address: '0xFc9e2C63370D8deb3521922a7B2b60f4Cff7e75a', +// lpToken: new Token( +// ChainId.MAINNET, +// '0xAfFD8d6b5e5A0e25034DD3D075532F9CE01C305c', +// 18, +// 'MobLP', +// 'Mobius pCelo V2 LP' +// ), +// swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), +// rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// lendingPrecision: JSBI.BigInt('1'), +// precision: JSBI.BigInt('18'), +// feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// feeIndex: 0, +// decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], +// peggedTo: 'Celo', +// pegComesAfter: true, +// displayDecimals: 0, +// gaugeAddress: '0xD0d57a6689188F854F996BEAE0Cb1949FDB5FF86', +// additionalRewards: ['0x00400FcbF0816bebB94654259de7273f4A05c762'], +// additionalRewardRate: ['6283068780000000'], +// displayChain: Chain.Celo, +// coin: Coins.Celo, +// }, +// { +// name: 'Poof cEUR V2', +// warningType: WarningType.POOF, +// tokenAddresses: ['0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73', '0xD8761DD6c7cB54febD33adD699F5E4440b62E01B'], +// tokens: [ +// new WrappedTokenInfo( +// { +// address: '0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73', +// name: 'Celo Euro', +// symbol: 'cEUR', +// chainId: 42220, +// decimals: 18, +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cEUR.png', +// }, +// [] +// ), +// new WrappedTokenInfo( +// { +// address: '0xD8761DD6c7cB54febD33adD699F5E4440b62E01B', +// name: 'Poof EUR', +// symbol: 'pEUR', +// chainId: 42220, +// decimals: 18, +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pEUR.png', +// }, +// [] +// ), +// ], +// address: '0x23C95678862a229fAC088bd9705622d78130bC3e', +// lpToken: new Token( +// ChainId.MAINNET, +// '0xec8e37876Fd9De919B58788B87A078e546149F87', +// 18, +// 'MobLP', +// 'Mobius pEUR V2 LP' +// ), +// swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), +// rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// lendingPrecision: JSBI.BigInt('1'), +// precision: JSBI.BigInt('18'), +// feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// feeIndex: 0, +// decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], +// peggedTo: '€', +// pegComesAfter: false, +// displayDecimals: 0, +// gaugeAddress: '0xCAEd243de23264Bdd8297c6eECcF320846eee18A', +// additionalRewards: ['0x00400FcbF0816bebB94654259de7273f4A05c762'], +// additionalRewardRate: ['6283068780000000'], +// // additionalRewards: [''], +// // additionalRewardRate: ['730282730000000'], +// displayChain: Chain.Celo, +// coin: Coins.Eur, +// }, +// { +// name: 'Poof cUSD V1', +// warningType: WarningType.POOF, +// tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0xB4aa2986622249B1F45eb93F28Cfca2b2606d809'], +// tokens: [ +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', +// decimals: 18, +// symbol: 'cUSD', +// name: 'Celo Dollar', +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', +// }, +// [] +// ), +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0xB4aa2986622249B1F45eb93F28Cfca2b2606d809', +// decimals: 18, +// symbol: 'pUSDxV1', +// name: 'Poof USD V1', +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pUSD.png', +// }, +// [] +// ), +// ], +// address: '0x02Db089fb09Fda92e05e92aFcd41D9AAfE9C7C7C', +// lpToken: new Token( +// ChainId.MAINNET, +// '0x18d71b8664E69D6Dd61C79247dBf12bFAaf66C10', +// 18, +// 'MobLP', +// 'Mobius pUSD V1 LP' +// ), +// swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), +// rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// lendingPrecision: JSBI.BigInt('1'), +// precision: JSBI.BigInt('18'), +// feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// feeIndex: 0, +// decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], +// peggedTo: '$', +// pegComesAfter: false, +// displayDecimals: 0, +// gaugeAddress: '0x2459BDb59a3BF6Ab6C412Ac0b220e7CDA1D4ea26', +// additionalRewards: ['0x00400FcbF0816bebB94654259de7273f4A05c762', '0x17700282592D6917F6A73D0bF8AcCf4D578c131e'], +// additionalRewardRate: ['0', '0'], +// displayChain: Chain.Celo, +// coin: Coins.USD, +// isKilled: true, +// }, +// { +// name: 'asUSDC (AllBridge)', +// tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0xCD7D7Ff64746C1909E44Db8e95331F9316478817'], +// tokens: [ +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', +// decimals: 18, +// symbol: 'cUSD', +// name: 'Celo Dollar', +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', +// }, +// [] +// ), +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0xCD7D7Ff64746C1909E44Db8e95331F9316478817', +// decimals: 18, +// symbol: 'asUSDC', +// name: 'US Dollar Coin (Solana AllBridge)', +// logoURI: 'https://bit.ly/3CwGimW', +// }, +// [] +// ), +// ], +// address: '0x63C1914bf00A9b395A2bF89aaDa55A5615A3656e', +// lpToken: new Token( +// ChainId.MAINNET, +// '0xAFEe90ab6A2D3B265262f94F6e437E7f6d94e26E', +// 18, +// 'MobLP', +// 'Mobius cUSD/asUSDC LP' +// ), +// swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), +// rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// lendingPrecision: JSBI.BigInt('1'), +// precision: JSBI.BigInt('18'), +// feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// feeIndex: 0, +// decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], +// peggedTo: '$', +// pegComesAfter: false, +// displayDecimals: 0, +// gaugeAddress: '0x27D9Bfa5F864862BeDC23cFab7e00b6b94488CC6', +// additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], +// additionalRewardRate: [weeklyEmissionToSeconds(3780)], +// displayChain: Chain.Solana, +// coin: Coins.USD, +// }, +// { +// name: 'pUSDC (Optics V1)', +// tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0xcC82628f6A8dEFA1e2B0aD7ed448bef3647F7941'], +// tokens: [ +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', +// decimals: 18, +// symbol: 'cUSD', +// name: 'Celo Dollar', +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', +// }, +// [] +// ), +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0xcC82628f6A8dEFA1e2B0aD7ed448bef3647F7941', +// decimals: 6, +// symbol: 'pUSDCxV1', +// name: 'USD Coin (PoS Optics)', +// logoURI: 'https://bit.ly/3CwGimW', +// }, +// [] +// ), +// ], +// address: '0x2080AAa167e2225e1FC9923250bA60E19a180Fb2', +// lpToken: new Token( +// ChainId.MAINNET, +// '0xf5b454cF47Caca418D95930AA03975Ee4bf409bc', +// 18, +// 'MobLP', +// 'Mobius cUSD/pUSDC LP' +// ), +// swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), +// rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// lendingPrecision: JSBI.BigInt('1'), +// precision: JSBI.BigInt('18'), +// feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// feeIndex: 0, +// decimals: [JSBI.BigInt('18'), JSBI.BigInt('6')], +// peggedTo: '$', +// pegComesAfter: false, +// displayDecimals: 0, +// gaugeAddress: '0x52517feb1Fc6141d5CF6718111C7Cc0FD764fA5d', +// additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], +// additionalRewardRate: ['0'], // ['2190848200000000'], +// displayChain: Chain.Polygon, +// coin: Coins.USD, +// isKilled: true, +// }, +// { +// name: 'wBTC (Optics V1)', +// tokenAddresses: ['0xD629eb00dEced2a080B7EC630eF6aC117e614f1b', '0xBe50a3013A1c94768A1ABb78c3cB79AB28fc1aCE'], +// tokens: [ +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0xD629eb00dEced2a080B7EC630eF6aC117e614f1b', +// decimals: 18, +// symbol: 'cBTC', +// name: 'Wrapped Bitcoin', +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cBTC.png', +// }, +// [] +// ), +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0xBe50a3013A1c94768A1ABb78c3cB79AB28fc1aCE', +// decimals: 8, +// symbol: 'wBTCxV1', +// name: 'Wrapped Bitcoin (Optics Bridge)', +// logoURI: 'https://etherscan.io/token/images/wbtc_28.png?v=1', +// }, +// [] +// ), +// ], +// address: '0x19260b9b573569dDB105780176547875fE9fedA3', +// lpToken: new Token( +// ChainId.MAINNET, +// '0x8cD0E2F11ed2E896a8307280dEEEE15B27e46BbE', +// 18, +// 'MobLP', +// 'Mobius cBTC/wBTC LP' +// ), +// swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), +// rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// lendingPrecision: JSBI.BigInt('1'), +// precision: JSBI.BigInt('18'), +// feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// feeIndex: 0, +// decimals: [JSBI.BigInt('18'), JSBI.BigInt('8')], +// peggedTo: '₿', +// pegComesAfter: true, +// displayDecimals: 2, +// gaugeAddress: '0x1A8938a37093d34581B21bAd2AE7DC1c19150C05', +// displayChain: Chain.Ethereum, +// coin: Coins.Bitcoin, +// isKilled: true, +// }, +// { +// name: 'WETH (Optics V1)', +// tokenAddresses: ['0x2DEf4285787d58a2f811AF24755A8150622f4361', '0xE919F65739c26a42616b7b8eedC6b5524d1e3aC4'], +// tokens: [ +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0x2DEf4285787d58a2f811AF24755A8150622f4361', +// decimals: 18, +// symbol: 'cETH', +// name: 'Wrapped Ethereum', +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cETH.svg', +// }, +// [] +// ), +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0xE919F65739c26a42616b7b8eedC6b5524d1e3aC4', +// decimals: 18, +// symbol: 'wETHxV1', +// name: 'Wrapped Ether (Optics Bridge)', +// logoURI: 'https://etherscan.io/token/images/weth_28.png', +// }, +// [] +// ), +// ], +// address: '0xE0F2cc70E52f05eDb383313393d88Df2937DA55a', +// lpToken: new Token( +// ChainId.MAINNET, +// '0x846b784Ab5302155542c1B3952B54305F220fd84', +// 18, +// 'MobLP', +// 'Mobius cETH/wETH LP' +// ), +// swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), +// rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// lendingPrecision: JSBI.BigInt('1'), +// precision: JSBI.BigInt('18'), +// feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// feeIndex: 0, +// decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], +// peggedTo: 'Ξ', +// pegComesAfter: true, +// displayDecimals: 2, +// gaugeAddress: '0xD38e76E17E66b562B61c149Ca0EE53CEa1145733', +// displayChain: Chain.Ethereum, +// coin: Coins.Ether, +// isKilled: true, +// }, +// { +// name: 'USDT (Moss)', +// tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0xcFFfE0c89a779c09Df3DF5624f54cDf7EF5fDd5D'], +// tokens: [ +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', +// decimals: 18, +// symbol: 'cUSD', +// name: 'Celo Dollar', +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', +// }, +// [] +// ), +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0xcFFfE0c89a779c09Df3DF5624f54cDf7EF5fDd5D', +// decimals: 18, +// symbol: 'cUSDTm', +// name: 'Tether (Moss Bridge)', +// logoURI: 'https://bit.ly/3AMrCyD', +// }, +// [] +// ), +// ], +// address: '0xdBF27fD2a702Cc02ac7aCF0aea376db780D53247', +// lpToken: new Token( +// ChainId.MAINNET, +// '0xC7a4c6EF4A16Dc24634Cc2A951bA5Fec4398f7e0', +// 18, +// 'MobLP', +// 'Mobius cUSD/cUSDT LP' +// ), +// swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), +// rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// lendingPrecision: JSBI.BigInt('1'), +// precision: JSBI.BigInt('18'), +// feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// feeIndex: 0, +// decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], +// peggedTo: '$', +// pegComesAfter: false, +// displayDecimals: 0, +// gaugeAddress: '0xe2d6095685248F38Ae9fef1b360D772b78Ea19D1', +// displayChain: Chain.Ethereum, +// coin: Coins.USD, +// }, +// { +// name: 'USDC (Moss)', +// tokenAddresses: ['0x765DE816845861e75A25fCA122bb6898B8B1282a', '0x93DB49bE12B864019dA9Cb147ba75cDC0506190e'], +// tokens: [ +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', +// decimals: 18, +// symbol: 'cUSD', +// name: 'Celo Dollar', +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png', +// }, +// [] +// ), +// new WrappedTokenInfo( +// { +// chainId: ChainId.MAINNET, +// address: '0x93DB49bE12B864019dA9Cb147ba75cDC0506190e', +// decimals: 18, +// symbol: 'cUSDCm', +// name: 'US Dollar Coin (Moss Bridge)', +// logoURI: 'https://bit.ly/3CwGimW', +// }, +// [] +// ), +// ], +// address: '0x0ff04189Ef135b6541E56f7C638489De92E9c778', +// lpToken: new Token( +// ChainId.MAINNET, +// '0x635aec36c4b61bac5eB1C3EEe191147d006F8a21', +// 18, +// 'MobLP', +// 'Mobius cUSD/cUSDC LP' +// ), +// swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), +// rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// lendingPrecision: JSBI.BigInt('1'), +// precision: JSBI.BigInt('18'), +// feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// feeIndex: 0, +// decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], +// peggedTo: '$', +// pegComesAfter: false, +// displayDecimals: 0, +// gaugeAddress: '0xd1B3C05FE24bda6F52e704daf1ACBa8c440d8573', +// additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], +// additionalRewardRate: ['0'], //['730282730000000'], +// displayChain: Chain.Ethereum, +// coin: Coins.USD, +// }, +// { +// name: 'Poof CELO V1', +// warningType: WarningType.POOF, +// tokenAddresses: ['0x471EcE3750Da237f93B8E339c536989b8978a438', '0xE74AbF23E1Fdf7ACbec2F3a30a772eF77f1601E1'], +// tokens: [ +// new WrappedTokenInfo( +// { +// address: '0x471EcE3750Da237f93B8E339c536989b8978a438', +// name: 'Celo', +// symbol: 'CELO', +// chainId: 42220, +// decimals: 18, +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_CELO.png', +// }, +// [] +// ), +// new WrappedTokenInfo( +// { +// address: '0xE74AbF23E1Fdf7ACbec2F3a30a772eF77f1601E1', +// name: 'Poof Celo V1', +// symbol: 'pCELOxV1', +// chainId: 42220, +// decimals: 18, +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pCELO.png', +// }, +// [] +// ), +// ], +// address: '0x413FfCc28e6cDDE7e93625Ef4742810fE9738578', +// lpToken: new Token( +// ChainId.MAINNET, +// '0x4D6B17828d0173668e8Eb730106444556a98c0F9', +// 18, +// 'MobLP', +// 'Mobius pCelo V1 LP' +// ), +// swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), +// rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// lendingPrecision: JSBI.BigInt('1'), +// precision: JSBI.BigInt('18'), +// feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// feeIndex: 0, +// decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], +// peggedTo: 'Celo', +// pegComesAfter: true, +// displayDecimals: 0, +// gaugeAddress: '0x5489b2F0A1992b889F47601D71E068Fd15c63f26', +// additionalRewards: ['0x00400FcbF0816bebB94654259de7273f4A05c762', '0x17700282592D6917F6A73D0bF8AcCf4D578c131e'], +// additionalRewardRate: ['0', '0'], +// // additionalRewards: [''], +// // additionalRewardRate: ['730282730000000'], +// displayChain: Chain.Celo, +// coin: Coins.Celo, +// isKilled: true, +// }, +// { +// name: 'Poof cEUR V1', +// warningType: WarningType.POOF, +// tokenAddresses: ['0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73', '0x56072D4832642dB29225dA12d6Fd1290E4744682'], +// tokens: [ +// new WrappedTokenInfo( +// { +// address: '0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73', +// name: 'Celo Euro', +// symbol: 'cEUR', +// chainId: 42220, +// decimals: 18, +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cEUR.png', +// }, +// [] +// ), +// new WrappedTokenInfo( +// { +// address: '0x56072D4832642dB29225dA12d6Fd1290E4744682', +// name: 'Poof EUR V1', +// symbol: 'pEURxV1', +// chainId: 42220, +// decimals: 18, +// logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pEUR.png', +// }, +// [] +// ), +// ], +// address: '0x382Ed834c6b7dBD10E4798B08889eaEd1455E820', +// lpToken: new Token( +// ChainId.MAINNET, +// '0x2642Ab16Bfb7A8b36EE42c9CbA2289C4Ca9F33b9', +// 18, +// 'MobLP', +// 'Mobius pEUR V1 LP' +// ), +// swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), +// rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// lendingPrecision: JSBI.BigInt('1'), +// precision: JSBI.BigInt('18'), +// feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// feeIndex: 0, +// decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], +// peggedTo: '€', +// pegComesAfter: false, +// displayDecimals: 0, +// gaugeAddress: '0xCF34F4ec5DC9E09428A4f4a45475f6277694166c', +// additionalRewards: ['0x00400FcbF0816bebB94654259de7273f4A05c762', '0x17700282592D6917F6A73D0bF8AcCf4D578c131e'], +// additionalRewardRate: ['0', '0'], +// // additionalRewards: [''], +// // additionalRewardRate: ['730282730000000'], +// displayChain: Chain.Celo, +// coin: Coins.Eur, +// isKilled: true, +// }, +// // { +// // name: 'Poof cUSD V1 [DISABLED]', +// // warningType: WarningType.POOF, +// // tokenAddresses: ['0xB4aa2986622249B1F45eb93F28Cfca2b2606d809'], +// // tokens: [ +// // new WrappedTokenInfo( +// // { +// // chainId: ChainId.MAINNET, +// // address: '0xB4aa2986622249B1F45eb93F28Cfca2b2606d809', +// // decimals: 18, +// // symbol: 'pUSDxV1', +// // name: 'Poof USD V1', +// // logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pUSD.png', +// // }, +// // [] +// // ), +// // new WrappedTokenInfo( +// // { +// // chainId: ChainId.MAINNET, +// // address: '0xd7Bf6946b740930c60131044bD2F08787e1DdBd4', +// // decimals: 18, +// // symbol: 'Mob LP', +// // name: 'Mobius USDC LP', +// // logoURI: 'https://bit.ly/3CwGimW', +// // }, +// // [] +// // ), +// // ], +// // address: '0x81B6a3d9f725AB5d706d9e552b128bC5bB0B58a1', +// // lpToken: new Token( +// // ChainId.MAINNET, +// // '0x57f008172cF89b972db3db7dD032e66BE4AF1A8c', +// // 18, +// // 'MobLP', +// // 'Mobius pUSD Meta LP' +// // ), +// // swapFee: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('7')), +// // rates: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// // lendingPrecision: JSBI.BigInt('1'), +// // precision: JSBI.BigInt('18'), +// // feeDenominator: JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('10')), +// // precisionMul: [JSBI.BigInt('1'), JSBI.BigInt('1')], +// // feeIndex: 0, +// // decimals: [JSBI.BigInt('18'), JSBI.BigInt('18')], +// // peggedTo: '$', +// // pegComesAfter: false, +// // displayDecimals: 0, +// // gaugeAddress: '0x1250D6dd3B51D20c14a8ECb10CC2dd713967767e', +// // metaPool: 'USDC (Optics)', +// // displayChain: Chain.Celo, +// // coin: Coins.USD, +// // disabled: true, +// // isKilled: true, +// // }, +// ], +// [ChainId.ALFAJORES]: [], +// [ChainId.BAKLAVA]: [], +// } diff --git a/src/constants/governance.ts b/src/constants/governance.ts index ae7d1e2a81e..6c3b2d0a976 100644 --- a/src/constants/governance.ts +++ b/src/constants/governance.ts @@ -3,6 +3,8 @@ export const GOVERNANCE_ADDRESS: { [chainId: number]: string } = { [44787]: '0xA878C6787490c9f0d2406bcd161b61f128Ab2708', } +export const GOVERNANCE_GENESIS = 10609767 + export const DEFAULT_AVERAGE_BLOCK_TIME_IN_SECS = 5 // Block time here is slightly higher (~1s) than average in order to avoid ongoing proposals past the displayed time diff --git a/src/constants/index.ts b/src/constants/index.ts index e28e9aec38b..53d2c14e442 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1,4 +1,5 @@ -import { ChainId, JSBI, Percent, Token } from '@ubeswap/sdk' +import JSBI from 'jsbi' +import { ChainId, Percent, Token } from 'lib/token-utils' export { MOBI } from './tokens' @@ -22,7 +23,7 @@ export const POOL_PROXY = '0x1bc2DbB8c4d04AaCF4A7fefcDB060766964B5237' // used for display in the default list when adding liquidity export const SUGGESTED_BASES: ChainTokenList = {} -export const CHAIN = ChainId.MAINNET +export const CHAIN = ChainId.Mainnet // default allowed slippage, in bips export const INITIAL_ALLOWED_SLIPPAGE = 50 diff --git a/src/constants/lists.ts b/src/constants/lists.ts deleted file mode 100644 index 87a4c7814f6..00000000000 --- a/src/constants/lists.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @TODO add list from blockchain association - */ -export const UNSUPPORTED_LIST_URLS: string[] = [] - -const UBESWAP_LIST = 'https://raw.githubusercontent.com/Ubeswap/default-token-list/master/ubeswap.token-list.json' -const UBESWAP_EXPERIMENTAL_LIST = - 'https://raw.githubusercontent.com/Ubeswap/default-token-list/master/ubeswap-experimental.token-list.json' - -// lower index == higher priority for token import -export const DEFAULT_LIST_OF_LISTS: string[] = [ - UBESWAP_LIST, - UBESWAP_EXPERIMENTAL_LIST, - ...UNSUPPORTED_LIST_URLS, // need to load unsupported tokens as well -] - -// default lists to be 'active' aka searched across -export const DEFAULT_ACTIVE_LIST_URLS: string[] = [UBESWAP_LIST] diff --git a/src/constants/mento.ts b/src/constants/mento.ts index 0c65e6bf1b4..81ddc3c192d 100644 --- a/src/constants/mento.ts +++ b/src/constants/mento.ts @@ -1,5 +1,5 @@ import { CeloContract, StableToken } from '@celo/contractkit' -import { ChainId, Percent, TokenAmount } from '@ubeswap/sdk' +import { ChainId, Percent, TokenAmount } from 'lib/token-utils' /** * Static definition of a Mento exchange. @@ -20,7 +20,7 @@ export interface IMentoExchangeInfo { } export const MENTO_POOL_INFO: { [K in ChainId]: IMentoExchange[] } = { - [ChainId.MAINNET]: [ + [ChainId.Mainnet]: [ { stable: StableToken.cUSD, contract: CeloContract.Exchange, @@ -34,6 +34,6 @@ export const MENTO_POOL_INFO: { [K in ChainId]: IMentoExchange[] } = { contract: CeloContract.ExchangeBRL, }, ], - [ChainId.ALFAJORES]: [], - [ChainId.BAKLAVA]: [], + [ChainId.Alfajores]: [], + [ChainId.Baklava]: [], } diff --git a/src/constants/multicall/index.ts b/src/constants/multicall/index.ts index 83076eb0d6b..bc45363a506 100644 --- a/src/constants/multicall/index.ts +++ b/src/constants/multicall/index.ts @@ -1,11 +1,11 @@ -import { ChainId } from '@ubeswap/sdk' +import { ChainId } from 'lib/token-utils' import MULTICALL_ABI from './abi.json' const MULTICALL_NETWORKS: { [chainId in ChainId]: string } = { - [ChainId.MAINNET]: '0x75f59534dd892c1f8a7b172d639fa854d529ada3', - [ChainId.ALFAJORES]: '0x75f59534dd892c1f8a7b172d639fa854d529ada3', - [ChainId.BAKLAVA]: '0x75f59534dd892c1f8a7b172d639fa854d529ada3', + [ChainId.Mainnet]: '0x75f59534dd892c1f8a7b172d639fa854d529ada3', + [ChainId.Alfajores]: '0x75f59534dd892c1f8a7b172d639fa854d529ada3', + [ChainId.Baklava]: '0x75f59534dd892c1f8a7b172d639fa854d529ada3', } export { MULTICALL_ABI, MULTICALL_NETWORKS } diff --git a/src/constants/poolManager.ts b/src/constants/poolManager.ts deleted file mode 100644 index ce376418686..00000000000 --- a/src/constants/poolManager.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ChainId } from '@ubeswap/sdk' - -//todo: replace Mainnet and Baklava PoolManager Addresses -export const POOL_MANAGER = { - [ChainId.MAINNET]: '0x9Ee3600543eCcc85020D6bc77EB553d1747a65D2', - [ChainId.ALFAJORES]: '0x9Ee3600543eCcc85020D6bc77EB553d1747a65D2', -} diff --git a/src/constants/pools.ts b/src/constants/pools.ts index 9afd64cc670..98ae3cf3e57 100644 --- a/src/constants/pools.ts +++ b/src/constants/pools.ts @@ -1,5 +1,13 @@ -import { ChainId, cUSD, Percent, Token, TokenAmount } from '@ubeswap/sdk' -import type JSBI from 'jsbi' +import BigNumber from 'bignumber.js' +import JSBI from 'jsbi' +import { ChainId, Percent, Token, TokenAmount } from 'lib/token-utils' + +import celoLogo from '../assets/images/celo-chain-logo.png' +import ethLogo from '../assets/images/ethereum-chain-logo.png' +import polygonLogo from '../assets/images/polygon-chain-logo.png' +import terraLogo from '../assets/images/terra-logo.png' +import { CHAIN } from './' +import { CELO, CETH, CUSD, UST, WETH } from './tokens' export type Fees = { trade: Percent @@ -28,11 +36,16 @@ export interface IExchangeInfo { } export interface IGauge { - exchange: IExchange address: string - additionalRewards?: string[] - additionalRewardRate?: string[] - isKilled?: boolean + additionalRewards: TokenAmount[] +} + +export interface Volume { + volume: { + total: number + day: number + week: number + } | null } export enum Coins { @@ -43,7 +56,7 @@ export enum Coins { Eur, } -enum WarningType { +export enum WarningType { POOF = 'poof', } @@ -52,6 +65,7 @@ export interface Peg { symbol: string position: 'before' | 'after' decimals: number + priceQuery: string | null } const Bitcoin: Peg = { @@ -59,6 +73,7 @@ const Bitcoin: Peg = { symbol: '₿', position: 'after', decimals: 2, + priceQuery: 'bitcoin', } const Ether: Peg = { @@ -66,6 +81,7 @@ const Ether: Peg = { symbol: 'Ξ', position: 'after', decimals: 2, + priceQuery: 'ethereum', } const Dollar: Peg = { @@ -73,6 +89,7 @@ const Dollar: Peg = { symbol: '$', position: 'before', decimals: 0, + priceQuery: null, } const Celo: Peg = { @@ -80,6 +97,7 @@ const Celo: Peg = { symbol: 'Celo', position: 'after', decimals: 0, + priceQuery: 'celo', } const Euro: Peg = { @@ -87,6 +105,7 @@ const Euro: Peg = { symbol: '€', position: 'before', decimals: 0, + priceQuery: 'celo-euro', } export enum Chain { @@ -98,16 +117,24 @@ export enum Chain { Terra, } +export const ChainLogo: { [c in Chain]: string } = { + [Chain.Celo]: celoLogo, + [Chain.Ethereum]: ethLogo, + [Chain.Polygon]: polygonLogo, + [Chain.Solana]: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_SOL.png', + [Chain.Avax]: 'https://s2.coinmarketcap.com/static/img/coins/64x64/5805.png', + [Chain.Terra]: terraLogo, +} + export interface DisplayPool { name: string chain: Chain peg: Peg - pool: IGauge | IExchange + pool: IExchange + gauge: IGauge | null warningType?: WarningType } -const UST = new Token(ChainId.MAINNET, '0xEd193C4E69F591E42398eF54DEa65aa1bb02835c', 18, 'UST', 'UST') - const recommendedFeesRaw = { adminFeeNumerator: '50', adminFeeDenominator: '100', @@ -126,48 +153,352 @@ export const RECOMMENDED_FEES: Fees = { deposit: new Percent(recommendedFeesRaw.depositFeeNumerator, recommendedFeesRaw.depositFeeDenominator), } -export const Pools: { [K in ChainId]: IExchange[] } = { - [ChainId.MAINNET]: [ - { - address: '0x9F4AdBD0af281C69a582eB2E6fa2A594D4204CAe', - lpToken: new Token( - ChainId.MAINNET, - '0x9438e7281D7E3e99A9dD21e0EAd9c6a254e17ab2', - 18, - 'MobLP', - 'Mobius cUSD/aUST LP' - ), - tokens: [cUSD[ChainId.MAINNET], UST], - }, - ], - [ChainId.ALFAJORES]: [], - [ChainId.BAKLAVA]: [], +export const RECOMMENDED_AMP = JSBI.BigInt('100') + +function lp(chainId: ChainId, address: string, name: string): Token { + return new Token({ + chainId, + address, + decimals: 18, + symbol: 'MobLP', + name, + }) +} + +const weeklyEmissionToSeconds = (n: number) => { + const yearlyEmission = new BigNumber(`${n}e+18`).dividedBy(7 * 24 * 60 * 60) + return yearlyEmission.toFixed(0) } export const StablePools: { [K in ChainId]: DisplayPool[] } = { - [ChainId.MAINNET]: [ + [ChainId.Mainnet]: [ { name: 'UST (Allbridge)', chain: Chain.Terra, peg: Dollar, pool: { - exchange: { - address: '0x9F4AdBD0af281C69a582eB2E6fa2A594D4204CAe', - lpToken: new Token( - ChainId.MAINNET, - '0x9438e7281D7E3e99A9dD21e0EAd9c6a254e17ab2', - 18, - 'MobLP', - 'Mobius cUSD/aUST LP' - ), - tokens: [cUSD[ChainId.MAINNET], UST], - }, - address: '0x107F94409746E8c8E6eFF139A100D17D9ca7FdfE', - additionalRewards: ['0x471EcE3750Da237f93B8E339c536989b8978a438'], - additionalRewardRate: ['12000000000000000'], + address: '0x9F4AdBD0af281C69a582eB2E6fa2A594D4204CAe'.toLowerCase(), + lpToken: lp(ChainId.Mainnet, '0x9438e7281D7E3e99A9dD21e0EAd9c6a254e17ab2'.toLowerCase(), 'Mobius cUSD/aUST LP'), + tokens: [CUSD[ChainId.Mainnet], UST[ChainId.Mainnet]], + }, + gauge: { + address: '0x107F94409746E8c8E6eFF139A100D17D9ca7FdfE'.toLowerCase(), + additionalRewards: [new TokenAmount(CELO[CHAIN], weeklyEmissionToSeconds(2268))], + }, + }, + { + name: 'WETH (Optics V2)', + chain: Chain.Ethereum, + peg: Ether, + pool: { + address: '0x74ef28D635c6C5800DD3Cd62d4c4f8752DaACB09'.toLowerCase(), + lpToken: lp(ChainId.Mainnet, '0x4fF08e2a4E7114af4B575AeF9250144f95790982'.toLowerCase(), 'Mobius cETH/wETH LP'), + tokens: [CETH[ChainId.Mainnet], WETH[ChainId.Mainnet]], + }, + gauge: { + address: '0x487c30CB18AA9Ced435911E2B414e0e85D7E52bB'.toLowerCase(), + additionalRewards: [], }, }, + // { + // name: 'USDC (Optics V2)', + // chain: Chain.Ethereum, + // peg: Dollar, + // pool: { + // address: '0x9906589Ea8fd27504974b7e8201DF5bBdE986b03'.toLowerCase(), + // lpToken: lp( + // ChainId.Mainnet, + // '0x39b6F09ef97dB406ab78D869471adb2384C494E3'.toLowerCase(), + // 'Mobius cUSD/cUSDC LP' + // ), + // tokens: [CUSD[ChainId.Mainnet], USDC[ChainId.Mainnet]], + // }, + // gauge: { + // address: '0xc96AeeaFF32129da934149F6134Aa7bf291a754E'.toLowerCase(), + // additionalRewards: [new TokenAmount(CELO[CHAIN], weeklyEmissionToSeconds(11088))], + // }, + // }, + // { + // name: 'DAI (Optics V2)', + // chain: Chain.Ethereum, + // peg: Dollar, + // pool: { + // address: '0xF3f65dFe0c8c8f2986da0FEc159ABE6fd4E700B4'.toLowerCase(), + // lpToken: lp(ChainId.Mainnet, '0x274DD2dF039f1f6131419C82173D97770e6af6B7'.toLowerCase(), 'Mobius cUSD/cDAI LP'), + // tokens: [CUSD[ChainId.Mainnet], DAI[ChainId.Mainnet]], + // }, + // gauge: { + // address: '0xE1f9D952EecC07cfEFa69df9fBB0cEF260957119'.toLowerCase(), + // additionalRewards: [new TokenAmount(CELO[CHAIN], weeklyEmissionToSeconds(3780))], + // }, + // }, + // { + // name: 'WBTC (Optics V2)', + // chain: Chain.Ethereum, + // peg: Bitcoin, + // pool: { + // address: '0xaEFc4e8cF655a182E8346B24c8AbcE45616eE0d2'.toLowerCase(), + // lpToken: lp(ChainId.Mainnet, '0x20d7274C5aF4f9DE6e8C93025e44aF3979d9Ab2b'.toLowerCase(), 'Mobius cBTC/wBTC LP'), + // tokens: [CBTC[ChainId.Mainnet], WBTC[ChainId.Mainnet]], + // }, + // gauge: { + // address: '0x127b524c74C2162Ee4BB2e42d8d2eB9050C0293E'.toLowerCase(), + // additionalRewards: [], + // }, + // }, + // { + // name: 'pUSDC (Optics V2)', + // chain: Chain.Polygon, + // peg: Dollar, + // pool: { + // address: '0xcCe0d62Ce14FB3e4363Eb92Db37Ff3630836c252'.toLowerCase(), + // lpToken: lp( + // ChainId.Mainnet, + // '0x68b239b415970dD7a5234A9701cbB5BfaB544C7C'.toLowerCase(), + // 'Mobius cUSD/pUSDC LP' + // ), + // tokens: [CUSD[ChainId.Mainnet], PUSDC[ChainId.Mainnet]], + // }, + // gauge: { + // address: '0x0A125D473cd3b1968e728DDF7d424c928C09222A'.toLowerCase(), + // additionalRewards: [], + // }, + // }, + // { + // name: 'USDC (Optics V1)', + // chain: Chain.Ethereum, + // peg: Dollar, + // pool: { + // address: '0xA5037661989789d0310aC2B796fa78F1B01F195D'.toLowerCase(), + // lpToken: lp( + // ChainId.Mainnet, + // '0xd7Bf6946b740930c60131044bD2F08787e1DdBd4'.toLowerCase(), + // 'Mobius cUSD/cUSDC LP' + // ), + // tokens: [CUSD[ChainId.Mainnet], USDC1[ChainId.Mainnet]], + // }, + // gauge: { + // address: '0xdAA2ab880b7f3D5697e6F85e63c28b9120AA9E07'.toLowerCase(), + // additionalRewards: [], + // }, + // }, + // { + // name: 'aaUSDC (Allbridge)', + // chain: Chain.Avax, + // peg: Dollar, + // pool: { + // address: '0x0986B42F5f9C42FeEef66fC23eba9ea1164C916D'.toLowerCase(), + // lpToken: lp( + // ChainId.Mainnet, + // '0x730e677f39C4Ca96012c394B9Da09A025E922F81'.toLowerCase(), + // 'Mobius cUSD/aaUSDC LP' + // ), + // tokens: [CUSD[ChainId.Mainnet], AAUSDC[ChainId.Mainnet]], + // }, + // gauge: { + // address: '0xF2ae5c2D2D2eD13dd324C0942163054fc4A3D4d9'.toLowerCase(), + // additionalRewards: [], + // }, + // }, + // { + // name: 'Poof cUSD V2', + // chain: Chain.Celo, + // peg: Dollar, + // warningType: WarningType.POOF, + // pool: { + // address: '0xa2F0E57d4cEAcF025E81C76f28b9Ad6E9Fbe8735'.toLowerCase(), + // lpToken: lp(ChainId.Mainnet, '0x07e137E5605E15C93f22545868Fa70CECfCbbFFE'.toLowerCase(), 'Mobius cUSD/pUSD LP'), + // tokens: [CUSD[ChainId.Mainnet], PUSD[ChainId.Mainnet]], + // }, + // gauge: { + // address: '0xE7195E651Cc47853f0054d85c8ADFc79D532929f'.toLowerCase(), + // additionalRewards: [new TokenAmount(POOF[ChainId.Mainnet], '6283068780000000')], + // }, + // }, + // { + // name: 'Poof CELO V2', + // chain: Chain.Celo, + // peg: Celo, + // warningType: WarningType.POOF, + // pool: { + // address: '0xFc9e2C63370D8deb3521922a7B2b60f4Cff7e75a'.toLowerCase(), + // lpToken: lp( + // ChainId.Mainnet, + // '0xAfFD8d6b5e5A0e25034DD3D075532F9CE01C305c'.toLowerCase(), + // 'Mobius cUSD/pCELO LP' + // ), + // tokens: [CUSD[ChainId.Mainnet], PCELO[ChainId.Mainnet]], + // }, + // gauge: { + // address: '0xD0d57a6689188F854F996BEAE0Cb1949FDB5FF86'.toLowerCase(), + // additionalRewards: [new TokenAmount(POOF[ChainId.Mainnet], '6283068780000000')], + // }, + // }, + // { + // name: 'Poof cEUR V2', + // chain: Chain.Celo, + // peg: Euro, + // warningType: WarningType.POOF, + // pool: { + // address: '0x23C95678862a229fAC088bd9705622d78130bC3e'.toLowerCase(), + // lpToken: lp(ChainId.Mainnet, '0xec8e37876Fd9De919B58788B87A078e546149F87'.toLowerCase(), 'Mobius cUSD/pEUR LP'), + // tokens: [CEUR[ChainId.Mainnet], PEUR[ChainId.Mainnet]], + // }, + // gauge: { + // address: '0xCAEd243de23264Bdd8297c6eECcF320846eee18A'.toLowerCase(), + // additionalRewards: [new TokenAmount(POOF[ChainId.Mainnet], '6283068780000000')], + // }, + // }, + // { + // name: 'Poof cUSD V1', + // chain: Chain.Celo, + // peg: Dollar, + // warningType: WarningType.POOF, + // pool: { + // address: '0x02Db089fb09Fda92e05e92aFcd41D9AAfE9C7C7C'.toLowerCase(), + // lpToken: lp(ChainId.Mainnet, '0x18d71b8664E69D6Dd61C79247dBf12bFAaf66C10'.toLowerCase(), 'Mobius cUSD/pUSD LP'), + // tokens: [CUSD[ChainId.Mainnet], PUSD1[ChainId.Mainnet]], + // }, + // gauge: { + // address: '0x2459BDb59a3BF6Ab6C412Ac0b220e7CDA1D4ea26'.toLowerCase(), + // additionalRewards: [], + // }, + // }, + // { + // name: 'asUSDC (Allbridge)', + // chain: Chain.Solana, + // peg: Dollar, + // pool: { + // address: '0x63C1914bf00A9b395A2bF89aaDa55A5615A3656e'.toLowerCase(), + // lpToken: lp( + // ChainId.Mainnet, + // '0xAFEe90ab6A2D3B265262f94F6e437E7f6d94e26E'.toLowerCase(), + // 'Mobius cUSD/asUSDC LP' + // ), + // tokens: [CUSD[ChainId.Mainnet], ASUSDC[ChainId.Mainnet]], + // }, + // gauge: { + // address: '0x27D9Bfa5F864862BeDC23cFab7e00b6b94488CC6'.toLowerCase(), + // additionalRewards: [new TokenAmount(CELO[ChainId.Mainnet], weeklyEmissionToSeconds(3780))], + // }, + // }, + // { + // name: 'pUSDC (Optics V1)', + // chain: Chain.Polygon, + // peg: Dollar, + // pool: { + // address: '0x2080AAa167e2225e1FC9923250bA60E19a180Fb2'.toLowerCase(), + // lpToken: lp( + // ChainId.Mainnet, + // '0xf5b454cF47Caca418D95930AA03975Ee4bf409bc'.toLowerCase(), + // 'Mobius cUSD/pUSDC LP' + // ), + // tokens: [CUSD[ChainId.Mainnet], PUSDC1[ChainId.Mainnet]], + // }, + // gauge: { + // address: '0x52517feb1Fc6141d5CF6718111C7Cc0FD764fA5d'.toLowerCase(), + // additionalRewards: [], + // }, + // }, + // { + // name: 'wBTC (Optics V1)', + // chain: Chain.Ethereum, + // peg: Bitcoin, + // pool: { + // address: '0x19260b9b573569dDB105780176547875fE9fedA3'.toLowerCase(), + // lpToken: lp(ChainId.Mainnet, '0x8cD0E2F11ed2E896a8307280dEEEE15B27e46BbE'.toLowerCase(), 'Mobius cBTC/wBTC LP'), + // tokens: [CBTC[ChainId.Mainnet], WBTC1[ChainId.Mainnet]], + // }, + // gauge: { + // address: '0x1A8938a37093d34581B21bAd2AE7DC1c19150C05'.toLowerCase(), + // additionalRewards: [], + // }, + // }, + // { + // name: 'wETH (Optics V1)', + // chain: Chain.Ethereum, + // peg: Ether, + // pool: { + // address: '0xE0F2cc70E52f05eDb383313393d88Df2937DA55a'.toLowerCase(), + // lpToken: lp(ChainId.Mainnet, '0x846b784Ab5302155542c1B3952B54305F220fd84'.toLowerCase(), 'Mobius cETH/wETH LP'), + // tokens: [CETH[ChainId.Mainnet], WETH1[ChainId.Mainnet]], + // }, + // gauge: { + // address: '0xD38e76E17E66b562B61c149Ca0EE53CEa1145733'.toLowerCase(), + // additionalRewards: [], + // }, + // }, + // { + // name: 'USDT (Moss)', + // chain: Chain.Ethereum, + // peg: Dollar, + // pool: { + // address: '0xdBF27fD2a702Cc02ac7aCF0aea376db780D53247'.toLowerCase(), + // lpToken: lp( + // ChainId.Mainnet, + // '0xC7a4c6EF4A16Dc24634Cc2A951bA5Fec4398f7e0'.toLowerCase(), + // 'Mobius cUSD/cUSDT LP' + // ), + // tokens: [CUSD[ChainId.Mainnet], USDTM[ChainId.Mainnet]], + // }, + // gauge: { + // address: '0xe2d6095685248F38Ae9fef1b360D772b78Ea19D1'.toLowerCase(), + // additionalRewards: [], + // }, + // }, + // { + // name: 'USDC (Moss)', + // chain: Chain.Ethereum, + // peg: Dollar, + // pool: { + // address: '0x0ff04189Ef135b6541E56f7C638489De92E9c778'.toLowerCase(), + // lpToken: lp( + // ChainId.Mainnet, + // '0x635aec36c4b61bac5eB1C3EEe191147d006F8a21'.toLowerCase(), + // 'Mobius cUSD/cUSDC LP' + // ), + // tokens: [CUSD[ChainId.Mainnet], USDCM[ChainId.Mainnet]], + // }, + // gauge: { + // address: '0xd1B3C05FE24bda6F52e704daf1ACBa8c440d8573'.toLowerCase(), + // additionalRewards: [], + // }, + // }, + // { + // name: 'Poof Celo V1', + // chain: Chain.Celo, + // peg: Celo, + // warningType: WarningType.POOF, + // pool: { + // address: '0x413FfCc28e6cDDE7e93625Ef4742810fE9738578'.toLowerCase(), + // lpToken: lp( + // ChainId.Mainnet, + // '0x4D6B17828d0173668e8Eb730106444556a98c0F9'.toLowerCase(), + // 'Mobius Celo/pCelo LP' + // ), + // tokens: [CELO[ChainId.Mainnet], PCELO1[ChainId.Mainnet]], + // }, + // gauge: { + // address: '0x5489b2F0A1992b889F47601D71E068Fd15c63f26'.toLowerCase(), + // additionalRewards: [], + // }, + // }, + // { + // name: 'Poof cEUR V1', + // chain: Chain.Celo, + // peg: Euro, + // warningType: WarningType.POOF, + // pool: { + // address: '0x382Ed834c6b7dBD10E4798B08889eaEd1455E820'.toLowerCase(), + // lpToken: lp(ChainId.Mainnet, '0x2642Ab16Bfb7A8b36EE42c9CbA2289C4Ca9F33b9'.toLowerCase(), 'Mobius cEUR/pEUR LP'), + // tokens: [CEUR[ChainId.Mainnet], PEUR1[ChainId.Mainnet]], + // }, + // gauge: { + // address: '0xCF34F4ec5DC9E09428A4f4a45475f6277694166c'.toLowerCase(), + // additionalRewards: [], + // }, + // }, ], - [ChainId.ALFAJORES]: [], - [ChainId.BAKLAVA]: [], + [ChainId.Alfajores]: [], + [ChainId.Baklava]: [], } diff --git a/src/constants/staking.ts b/src/constants/staking.ts new file mode 100644 index 00000000000..e5772b58769 --- /dev/null +++ b/src/constants/staking.ts @@ -0,0 +1,29 @@ +import { ChainId } from 'lib/token-utils' + +import { CELO } from './tokens' + +export const ExternalRewardsToken = CELO + +export const ExternalStakingRewards: { [K in ChainId]: string } = { + [ChainId.Mainnet]: '0x0812f6de916667C5aa820E757704c4ac69159529', + [ChainId.Alfajores]: '', + [ChainId.Baklava]: '', +} + +export const MOBIUS_MINTER_ADDRESS: { [K in ChainId]: string } = { + [ChainId.Mainnet]: '0x5F0200CA03196D5b817E2044a0Bb0D837e0A7823', + [ChainId.Alfajores]: '0x5c212FA1cf8b1143f2142C26161e65404034c01f', + [ChainId.Baklava]: '', +} + +export const GAUGE_CONTROLLER: { [K in ChainId]: string } = { + [ChainId.Mainnet]: '0x7530E03056D3a8eD0323e61091ea2f17a1aC5C25', + [ChainId.Alfajores]: '0x00063Fbe0c90834EE90C6191d0D9F04eaB01A14f', + [ChainId.Baklava]: '', +} + +export const GAUGE_PROXY: { [K in ChainId]: string } = { + [ChainId.Mainnet]: '0x0a3Ac12422C95F84b5bD18A6d9904d132a161C68', + [ChainId.Alfajores]: '', + [ChainId.Baklava]: '', +} diff --git a/src/constants/tokens.ts b/src/constants/tokens.ts index 5b4418bdcfa..e68336b2e40 100644 --- a/src/constants/tokens.ts +++ b/src/constants/tokens.ts @@ -1,6 +1,6 @@ -import { ChainId } from '@ubeswap/sdk' +import { ChainId, Token } from 'lib/token-utils' import mapValues from 'lodash/mapValues' -import { WrappedTokenInfo } from 'state/lists/hooks' +import { VestType } from 'state/claim/reducer' const makeTokens = ( addresses: { [net in ChainId]: string }, @@ -8,20 +8,17 @@ const makeTokens = ( symbol: string, name: string, logoURI: string -): { [net in ChainId]: WrappedTokenInfo } => { +): { [net in ChainId]: Token } => { return mapValues(addresses, (tokenAddress, network) => { - return new WrappedTokenInfo( - { chainId: parseInt(network), address: tokenAddress, decimals, symbol, name, logoURI }, - [] - ) + return new Token({ chainId: parseInt(network), address: tokenAddress, decimals, symbol, name, logoURI }) }) } export const MOBI = makeTokens( { - [ChainId.MAINNET]: '0x73a210637f6F6B7005512677Ba6B3C96bb4AA44B', - [ChainId.ALFAJORES]: '0x17a139f275102bBaB5BcbF1c4b7143F08B635EA2', - [ChainId.BAKLAVA]: '0x00Be915B9dCf56a3CBE739D9B9c202ca692409EC', + [ChainId.Mainnet]: '0x73a210637f6F6B7005512677Ba6B3C96bb4AA44B', + [ChainId.Alfajores]: '0x17a139f275102bBaB5BcbF1c4b7143F08B635EA2', + [ChainId.Baklava]: '0x00Be915B9dCf56a3CBE739D9B9c202ca692409EC', }, 18, 'MOBI', @@ -31,9 +28,9 @@ export const MOBI = makeTokens( export const CELO = makeTokens( { - [ChainId.MAINNET]: '0x471EcE3750Da237f93B8E339c536989b8978a438', - [ChainId.ALFAJORES]: '0xF194afDf50B03e69Bd7D057c1Aa9e10c9954E4C9', - [ChainId.BAKLAVA]: '0xF194afDf50B03e69Bd7D057c1Aa9e10c9954E4C9', + [ChainId.Mainnet]: '0x471EcE3750Da237f93B8E339c536989b8978a438', + [ChainId.Alfajores]: '0xF194afDf50B03e69Bd7D057c1Aa9e10c9954E4C9', + [ChainId.Baklava]: '0xF194afDf50B03e69Bd7D057c1Aa9e10c9954E4C9', }, 18, 'CELO', @@ -43,9 +40,9 @@ export const CELO = makeTokens( export const CUSD = makeTokens( { - [ChainId.MAINNET]: '0x765DE816845861e75A25fCA122bb6898B8B1282a', - [ChainId.ALFAJORES]: '0x874069Fa1Eb16D44d622F2e0Ca25eeA172369bC1', - [ChainId.BAKLAVA]: '0x62492A644A588FD904270BeD06ad52B9abfEA1aE', + [ChainId.Mainnet]: '0x765DE816845861e75A25fCA122bb6898B8B1282a', + [ChainId.Alfajores]: '0x874069Fa1Eb16D44d622F2e0Ca25eeA172369bC1', + [ChainId.Baklava]: '0x62492A644A588FD904270BeD06ad52B9abfEA1aE', }, 18, 'cUSD', @@ -55,9 +52,9 @@ export const CUSD = makeTokens( export const CEUR = makeTokens( { - [ChainId.MAINNET]: '0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73', - [ChainId.ALFAJORES]: '0x10c892A6EC43a53E45D0B916B4b7D383B1b78C0F', - [ChainId.BAKLAVA]: '0xf9ecE301247aD2CE21894941830A2470f4E774ca', + [ChainId.Mainnet]: '0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73', + [ChainId.Alfajores]: '0x10c892A6EC43a53E45D0B916B4b7D383B1b78C0F', + [ChainId.Baklava]: '0xf9ecE301247aD2CE21894941830A2470f4E774ca', }, 18, 'cEUR', @@ -67,9 +64,9 @@ export const CEUR = makeTokens( export const CREAL = makeTokens( { - [ChainId.MAINNET]: '0xe8537a3d056DA446677B9E9d6c5dB704EaAb4787', - [ChainId.ALFAJORES]: '0xE4D517785D091D3c54818832dB6094bcc2744545', - [ChainId.BAKLAVA]: '0xf9ecE301247aD2CE21894941830A2470f4E774ca', + [ChainId.Mainnet]: '0xe8537a3d056DA446677B9E9d6c5dB704EaAb4787', + [ChainId.Alfajores]: '0xE4D517785D091D3c54818832dB6094bcc2744545', + [ChainId.Baklava]: '0xf9ecE301247aD2CE21894941830A2470f4E774ca', }, 18, 'cREAL', @@ -79,9 +76,9 @@ export const CREAL = makeTokens( export const VEMOBI = makeTokens( { - [ChainId.MAINNET]: '0xd813a846aA9D572140d7ABBB4eFaC8cD786b4c0E', - [ChainId.ALFAJORES]: '0x7d64708ecf5201cfE74364424AddB0A8FD32174f', - [ChainId.BAKLAVA]: '0xFe2434bcE62C9B4845fe0C57438f5F86fA4771A7', + [ChainId.Mainnet]: '0xd813a846aA9D572140d7ABBB4eFaC8cD786b4c0E', + [ChainId.Alfajores]: '0x7d64708ecf5201cfE74364424AddB0A8FD32174f', + [ChainId.Baklava]: '0xFe2434bcE62C9B4845fe0C57438f5F86fA4771A7', }, 18, 'veMOBI', @@ -89,42 +86,329 @@ export const VEMOBI = makeTokens( 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_MOBI.png' ) -export const ExternalRewards: { [K in ChainId]: WrappedTokenInfo[] } = { - [ChainId.MAINNET]: [ - new WrappedTokenInfo( - { - address: '0x00400FcbF0816bebB94654259de7273f4A05c762', - name: 'Poof', - symbol: 'POOF', - chainId: ChainId.MAINNET, - decimals: 18, - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_POOF.png', - }, - [] - ), - new WrappedTokenInfo( - { - address: '0x17700282592D6917F6A73D0bF8AcCf4D578c131e', - name: 'Moola', - symbol: 'MOO', - chainId: ChainId.MAINNET, - decimals: 18, - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_MOO.png', - }, - [] - ), - new WrappedTokenInfo( - { - address: '0x471EcE3750Da237f93B8E339c536989b8978a438', - name: 'Celo', - symbol: 'CELO', - chainId: ChainId.MAINNET, - decimals: 18, - logoURI: 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_CELO.png', - }, - [] - ), - ], - [ChainId.ALFAJORES]: [], - [ChainId.BAKLAVA]: [], +export const UST = makeTokens( + { + [ChainId.Mainnet]: '0xEd193C4E69F591E42398eF54DEa65aa1bb02835c', + [ChainId.Alfajores]: '0xEd193C4E69F591E42398eF54DEa65aa1bb02835c', + [ChainId.Baklava]: '0xEd193C4E69F591E42398eF54DEa65aa1bb02835c', + }, + 18, + 'UST', + 'Terra USD', + 'https://raw.githubusercontent.com/kyscott18/default-token-list/master/assets/asset_UST.png' +) + +export const CETH = makeTokens( + { + [ChainId.Mainnet]: '0x2DEf4285787d58a2f811AF24755A8150622f4361', + [ChainId.Alfajores]: '0x2DEf4285787d58a2f811AF24755A8150622f4361', + [ChainId.Baklava]: '0x2DEf4285787d58a2f811AF24755A8150622f4361', + }, + 18, + 'cETH', + 'Wrapped Ethereum', + 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cETH.svg' +) + +export const WETH = makeTokens( + { + [ChainId.Mainnet]: '0x122013fd7dF1C6F636a5bb8f03108E876548b455', + [ChainId.Alfajores]: '0x122013fd7dF1C6F636a5bb8f03108E876548b455', + [ChainId.Baklava]: '0x122013fd7dF1C6F636a5bb8f03108E876548b455', + }, + 18, + 'wETH', + 'Wrapped Ether (Optics Bridge)', + 'https://etherscan.io/token/images/weth_28.png' +) + +export const USDC = makeTokens( + { + [ChainId.Mainnet]: '0xef4229c8c3250C675F21BCefa42f58EfbfF6002a', + [ChainId.Alfajores]: '0xef4229c8c3250C675F21BCefa42f58EfbfF6002a', + [ChainId.Baklava]: '0xef4229c8c3250C675F21BCefa42f58EfbfF6002a', + }, + 6, + 'cUSDC', + 'US Dollar Coin (Optics Bridge)', + 'https://bit.ly/3CwGimW' +) + +export const DAI = makeTokens( + { + [ChainId.Mainnet]: '0x90Ca507a5D4458a4C6C6249d186b6dCb02a5BCCd', + [ChainId.Alfajores]: '0x90Ca507a5D4458a4C6C6249d186b6dCb02a5BCCd', + [ChainId.Baklava]: '0x90Ca507a5D4458a4C6C6249d186b6dCb02a5BCCd', + }, + 18, + 'DAI', + 'Optics DAI', + 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_DAI.png' +) + +export const CBTC = makeTokens( + { + [ChainId.Mainnet]: '0xD629eb00dEced2a080B7EC630eF6aC117e614f1b', + [ChainId.Alfajores]: '0xD629eb00dEced2a080B7EC630eF6aC117e614f1b', + [ChainId.Baklava]: '0xD629eb00dEced2a080B7EC630eF6aC117e614f1b', + }, + 18, + 'cBTC', + 'Wrapped Bitcoin', + 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cBTC.png' +) + +export const WBTC = makeTokens( + { + [ChainId.Mainnet]: '0xBAAB46E28388d2779e6E31Fd00cF0e5Ad95E327B', + [ChainId.Alfajores]: '0xBAAB46E28388d2779e6E31Fd00cF0e5Ad95E327B', + [ChainId.Baklava]: '0xBAAB46E28388d2779e6E31Fd00cF0e5Ad95E327B', + }, + 8, + 'wBTC', + 'Wrapped Bitcoin (Optics Bridge)', + 'https://etherscan.io/token/images/wbtc_28.png?v=1' +) + +export const PUSDC = makeTokens( + { + [ChainId.Mainnet]: '0x1bfc26cE035c368503fAE319Cc2596716428ca44', + [ChainId.Alfajores]: '0x1bfc26cE035c368503fAE319Cc2596716428ca44', + [ChainId.Baklava]: '0x1bfc26cE035c368503fAE319Cc2596716428ca44', + }, + 6, + 'pUSDC', + 'USD Coin (PoS Optics)', + 'https://bit.ly/3CwGimW' +) + +export const USDC1 = makeTokens( + { + [ChainId.Mainnet]: '0x2A3684e9Dc20B857375EA04235F2F7edBe818FA7', + [ChainId.Alfajores]: '0x2A3684e9Dc20B857375EA04235F2F7edBe818FA7', + [ChainId.Baklava]: '0x2A3684e9Dc20B857375EA04235F2F7edBe818FA7', + }, + 6, + 'cUSDCxV1', + 'US Dollar Coin (Optics Bridge)', + 'https://bit.ly/3CwGimW' +) + +export const AAUSDC = makeTokens( + { + [ChainId.Mainnet]: '0xb70e0a782b058BFdb0d109a3599BEc1f19328E36', + [ChainId.Alfajores]: '0xb70e0a782b058BFdb0d109a3599BEc1f19328E36', + [ChainId.Baklava]: '0xb70e0a782b058BFdb0d109a3599BEc1f19328E36', + }, + 18, + 'aaUSDC', + 'US Dollar Coin (Avalanche Allbridge)', + 'https://bit.ly/3CwGimW' +) + +export const PUSD = makeTokens( + { + [ChainId.Mainnet]: '0xEadf4A7168A82D30Ba0619e64d5BCf5B30B45226', + [ChainId.Alfajores]: '0xEadf4A7168A82D30Ba0619e64d5BCf5B30B45226', + [ChainId.Baklava]: '0xEadf4A7168A82D30Ba0619e64d5BCf5B30B45226', + }, + 18, + 'pUSD', + 'Poof USD V2', + 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pUSD.png' +) + +export const PCELO = makeTokens( + { + [ChainId.Mainnet]: '0x301a61D01A63c8D670c2B8a43f37d12eF181F997', + [ChainId.Alfajores]: '0x301a61D01A63c8D670c2B8a43f37d12eF181F997', + [ChainId.Baklava]: '0x301a61D01A63c8D670c2B8a43f37d12eF181F997', + }, + 18, + 'pCELO', + 'Poof CELO V2', + 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pCELO.png' +) + +export const PEUR = makeTokens( + { + [ChainId.Mainnet]: '0xD8761DD6c7cB54febD33adD699F5E4440b62E01B', + [ChainId.Alfajores]: '0xD8761DD6c7cB54febD33adD699F5E4440b62E01B', + [ChainId.Baklava]: '0xD8761DD6c7cB54febD33adD699F5E4440b62E01B', + }, + 18, + 'pEUR', + 'Poof EUR', + 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pEUR.png' +) + +export const PUSD1 = makeTokens( + { + [ChainId.Mainnet]: '0xB4aa2986622249B1F45eb93F28Cfca2b2606d809', + [ChainId.Alfajores]: '0xB4aa2986622249B1F45eb93F28Cfca2b2606d809', + [ChainId.Baklava]: '0xB4aa2986622249B1F45eb93F28Cfca2b2606d809', + }, + 18, + 'pUSDxV1', + 'Poof USD V1', + 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pUSD.png' +) + +export const ASUSDC = makeTokens( + { + [ChainId.Mainnet]: '0xCD7D7Ff64746C1909E44Db8e95331F9316478817', + [ChainId.Alfajores]: '0xCD7D7Ff64746C1909E44Db8e95331F9316478817', + [ChainId.Baklava]: '0xCD7D7Ff64746C1909E44Db8e95331F9316478817', + }, + 18, + 'asUSDC', + 'US Dollar Coin (Solana Allbridge)', + 'https://bit.ly/3CwGimW' +) + +export const PUSDC1 = makeTokens( + { + [ChainId.Mainnet]: '0xcC82628f6A8dEFA1e2B0aD7ed448bef3647F7941', + [ChainId.Alfajores]: '0xcC82628f6A8dEFA1e2B0aD7ed448bef3647F7941', + [ChainId.Baklava]: '0xcC82628f6A8dEFA1e2B0aD7ed448bef3647F7941', + }, + 6, + 'pUSDCxV1', + 'USD Coin (PoS Optics)', + 'https://bit.ly/3CwGimW' +) + +export const WBTC1 = makeTokens( + { + [ChainId.Mainnet]: '0xBe50a3013A1c94768A1ABb78c3cB79AB28fc1aCE', + [ChainId.Alfajores]: '0xBe50a3013A1c94768A1ABb78c3cB79AB28fc1aCE', + [ChainId.Baklava]: '0xBe50a3013A1c94768A1ABb78c3cB79AB28fc1aCE', + }, + 8, + 'wBTCxV1', + 'Wrapped Bitcoin (Optics Bridge)', + 'https://etherscan.io/token/images/wbtc_28.png?v=1' +) + +export const WETH1 = makeTokens( + { + [ChainId.Mainnet]: '0xE919F65739c26a42616b7b8eedC6b5524d1e3aC4', + [ChainId.Alfajores]: '0xE919F65739c26a42616b7b8eedC6b5524d1e3aC4', + [ChainId.Baklava]: '0xE919F65739c26a42616b7b8eedC6b5524d1e3aC4', + }, + 18, + 'wETHxV1', + 'Wrapped Ether (Optics Bridge)', + 'https://etherscan.io/token/images/weth_28.png' +) + +export const USDTM = makeTokens( + { + [ChainId.Mainnet]: '0xcFFfE0c89a779c09Df3DF5624f54cDf7EF5fDd5D', + [ChainId.Alfajores]: '0xcFFfE0c89a779c09Df3DF5624f54cDf7EF5fDd5D', + [ChainId.Baklava]: '0xcFFfE0c89a779c09Df3DF5624f54cDf7EF5fDd5D', + }, + 18, + 'cUSDTm', + 'Tether (Moss Bridge)', + 'https://bit.ly/3AMrCyD' +) + +export const USDCM = makeTokens( + { + [ChainId.Mainnet]: '0x93DB49bE12B864019dA9Cb147ba75cDC0506190e', + [ChainId.Alfajores]: '0x93DB49bE12B864019dA9Cb147ba75cDC0506190e', + [ChainId.Baklava]: '0x93DB49bE12B864019dA9Cb147ba75cDC0506190e', + }, + 18, + 'cUSDCm', + 'US Dollar Coin (Moss Bridge)', + 'https://bit.ly/3CwGimW' +) + +export const PCELO1 = makeTokens( + { + [ChainId.Mainnet]: '0xE74AbF23E1Fdf7ACbec2F3a30a772eF77f1601E1', + [ChainId.Alfajores]: '0xE74AbF23E1Fdf7ACbec2F3a30a772eF77f1601E1', + [ChainId.Baklava]: '0xE74AbF23E1Fdf7ACbec2F3a30a772eF77f1601E1', + }, + 18, + 'pCELOxV1', + 'Poof Celo V1', + 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pCELO.png' +) + +export const PEUR1 = makeTokens( + { + [ChainId.Mainnet]: '0x56072D4832642dB29225dA12d6Fd1290E4744682', + [ChainId.Alfajores]: '0x56072D4832642dB29225dA12d6Fd1290E4744682', + [ChainId.Baklava]: '0x56072D4832642dB29225dA12d6Fd1290E4744682', + }, + 18, + 'pEURxV1', + 'Poof EUR V1', + 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pEUR.png' +) + +export const POOF = makeTokens( + { + [ChainId.Mainnet]: '0x00400FcbF0816bebB94654259de7273f4A05c762', + [ChainId.Alfajores]: '0x00400FcbF0816bebB94654259de7273f4A05c762', + [ChainId.Baklava]: '0x00400FcbF0816bebB94654259de7273f4A05c762', + }, + 18, + 'Poof', + 'POOF', + 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_POOF.png' +) + +export const MOO = makeTokens( + { + [ChainId.Mainnet]: '0x17700282592D6917F6A73D0bF8AcCf4D578c131e', + [ChainId.Alfajores]: '0x17700282592D6917F6A73D0bF8AcCf4D578c131e', + [ChainId.Baklava]: '0x17700282592D6917F6A73D0bF8AcCf4D578c131e', + }, + 18, + 'Moola', + 'MOO', + 'https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_MOO.png' +) + +export const ExternalRewards: { [K in ChainId]: Token[] } = { + [ChainId.Mainnet]: [POOF[ChainId.Mainnet], MOO[ChainId.Mainnet], CELO[ChainId.Mainnet]], + [ChainId.Alfajores]: [], + [ChainId.Baklava]: [], +} + +type AddressMap = { [K in ChainId]: string } + +export const LP_VEST_ADDRESSES: AddressMap = { + [ChainId.Mainnet]: '0x74Fc71eF736feeaCfd58aeb2543c5fe4d33aDc14', + [ChainId.Alfajores]: '0x9ff6d45F5900D7aCBdCb6d79fFFf22C9F63dF040', + [ChainId.Baklava]: '', +} + +export const FOUNDER_VEST_ADDRESSES: AddressMap = { + [ChainId.Mainnet]: '0x34deFd314fa23821a87FCbF5393311Bc5B7608C1', + [ChainId.Alfajores]: '0x9ff6d45F5900D7aCBdCb6d79fFFf22C9F63dF040', + [ChainId.Baklava]: '', +} + +export const INVESTOR_VEST_ADDRESSES: AddressMap = { + [ChainId.Mainnet]: '0x5498248EaB20ff314bC465268920B48eed4Cdb7C', + [ChainId.Alfajores]: '0x9ff6d45F5900D7aCBdCb6d79fFFf22C9F63dF040', + [ChainId.Baklava]: '', +} + +export const ADVISOR_VEST_ADDRESSES: AddressMap = { + [ChainId.Mainnet]: '0x54Bf52862E1Fdf0D43D9B19Abb5ec72acA0a25A6', + [ChainId.Alfajores]: '0x9ff6d45F5900D7aCBdCb6d79fFFf22C9F63dF040', + [ChainId.Baklava]: '', +} + +export const VestingAddresses: { [type in VestType]: AddressMap } = { + [VestType.FOUNDER]: FOUNDER_VEST_ADDRESSES, + [VestType.ADVISOR]: ADVISOR_VEST_ADDRESSES, + [VestType.INVESTOR]: INVESTOR_VEST_ADDRESSES, + [VestType.LP]: LP_VEST_ADDRESSES, } diff --git a/src/data/Allowances.ts b/src/data/Allowances.ts index fed9d652294..cecd9b52385 100644 --- a/src/data/Allowances.ts +++ b/src/data/Allowances.ts @@ -1,5 +1,5 @@ -import { Token, TokenAmount } from '@ubeswap/sdk' import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp' +import { Token, TokenAmount } from 'lib/token-utils' import { useEffect, useMemo, useState } from 'react' import { useBlockNumber } from 'state/application/hooks' @@ -10,16 +10,15 @@ export function useTokenAllowance(token?: Token, owner?: string, spender?: strin const time = useCurrentBlockTimestamp() const block = useBlockNumber() - const inputs = useMemo(() => [owner, spender], [owner, spender]) const [allowance, setAllowance] = useState('') useEffect(() => { async function getAllowance() { + if (!owner || !spender) return const newAllowance = await contract?.allowance(owner, spender) setAllowance(newAllowance?.toString() ?? '0') } getAllowance() - }, [token, allowance, block, time]) - // const allowance = useSingleCallResult(contract, 'allowance', inputs).result + }, [token, allowance, block, time, owner, spender, contract]) return useMemo( () => (token && allowance ? new TokenAmount(token, allowance.toString()) : undefined), diff --git a/src/data/TotalSupply.ts b/src/data/TotalSupply.ts index 6ec319f636c..5cdae3ea294 100644 --- a/src/data/TotalSupply.ts +++ b/src/data/TotalSupply.ts @@ -1,5 +1,5 @@ import { BigNumber } from '@ethersproject/bignumber' -import { Token, TokenAmount } from '@ubeswap/sdk' +import { Token, TokenAmount } from 'lib/token-utils' import { useTokenContract } from '../hooks/useContract' import { useSingleCallResult } from '../state/multicall/hooks' diff --git a/src/hooks/Tokens.ts b/src/hooks/Tokens.ts index b3b6dd974f4..37bf5492afd 100644 --- a/src/hooks/Tokens.ts +++ b/src/hooks/Tokens.ts @@ -1,17 +1,31 @@ -import { Token } from '@ubeswap/sdk' import { MENTO_POOL_INFO } from 'constants/mento' +import { IExchange, StablePools } from 'constants/pools' +import { Token } from 'lib/token-utils' import { stableToToken } from 'state/mentoPools/hooks' import { dedupeTokens } from 'utils/tokens' import { CHAIN } from '../constants' -import { MOBI_TOKEN, STATIC_POOL_INFO } from '../constants/StablePools' -import { CELO, ExternalRewards, VEMOBI } from '../constants/tokens' +import { CELO, ExternalRewards, MOBI, VEMOBI } from '../constants/tokens' import { isAddress } from '../utils' +function inPool(token: Token, pool: IExchange): boolean { + return pool.tokens.map((t) => t.address === token.address && t.chainId === token.chainId).includes(true) +} + +export function useTokensTradeable(mento: boolean, tokenIn: Token | null | undefined): Token[] { + if (!tokenIn) return [] + const pools = mento + ? tokenIn === CELO[CHAIN] + ? MENTO_POOL_INFO[CHAIN].map((m) => stableToToken(m.stable)) + : [CELO[CHAIN]] + : StablePools[CHAIN].filter((display) => inPool(tokenIn, display.pool)) + .flatMap((display) => display.pool.tokens) + .filter((token) => token !== tokenIn) + return dedupeTokens(pools) +} + export function useSwappableTokens(mento: boolean): Token[] { - return dedupeTokens( - mento ? getMentoTokens() : STATIC_POOL_INFO[CHAIN].filter((pool) => !pool.disabled).flatMap(({ tokens }) => tokens) - ) + return dedupeTokens(mento ? getMentoTokens() : StablePools[CHAIN].flatMap((display) => display.pool.tokens)) } export function getMentoTokens(): Token[] { @@ -19,7 +33,7 @@ export function getMentoTokens(): Token[] { } export function getAllTokens(): Token[] | null { - const StableTokensWithDup = STATIC_POOL_INFO[CHAIN].flatMap((pools) => pools.tokens) + const StableTokensWithDup = StablePools[CHAIN].flatMap((display) => display.pool.tokens) const MentoTokensWithDup = getMentoTokens() return dedupeTokens(MentoTokensWithDup.concat(StableTokensWithDup).concat(ExternalRewards[CHAIN])) } @@ -40,10 +54,10 @@ export function useCurrency(currencyId: string | undefined): Token | null { return token } -export function useMobi(): Token | undefined { - return MOBI_TOKEN[CHAIN] +export function useMobi(): Token { + return MOBI[CHAIN] } -export function useVeMobi(): Token | undefined { +export function useVeMobi(): Token { return VEMOBI[CHAIN] } diff --git a/src/hooks/useApproveCallback.ts b/src/hooks/useApproveCallback.ts index a32770667f1..de68b7a61fa 100644 --- a/src/hooks/useApproveCallback.ts +++ b/src/hooks/useApproveCallback.ts @@ -1,13 +1,10 @@ import { MaxUint256 } from '@ethersproject/constants' -import { TokenAmount } from '@ubeswap/sdk' +import { TokenAmount } from 'lib/token-utils' import { useCallback, useMemo } from 'react' -import { MobiusTrade } from 'state/swap/hooks' import { useUserMinApprove } from 'state/user/hooks' import { useTokenAllowance } from '../data/Allowances' -import { Field } from '../state/swap/actions' import { useHasPendingApproval } from '../state/transactions/hooks' -import { computeSlippageAdjustedAmounts } from '../utils/prices' import { useTokenContract } from './useContract' import { useDoTransaction } from './useDoTransaction' import { useWeb3Context } from './web3' @@ -79,13 +76,13 @@ export function useApproveCallback( if (minApprove) { await doTransaction(tokenContract, 'approve', { args: [spender, amountToApprove.raw.toString()], - summary: `Approve ${amountToApprove.toSignificant(6)} ${amountToApprove.currency.symbol}`, + summary: `Approve ${amountToApprove.toSignificant(6)} ${amountToApprove.token.symbol}`, approval: { tokenAddress: token.address, spender: spender }, }) } else { await doTransaction(tokenContract, 'approve', { args: [spender, MaxUint256], - summary: `Approve ${amountToApprove.currency.symbol}`, + summary: `Approve ${amountToApprove.token.symbol}`, approval: { tokenAddress: token.address, spender: spender }, }) } @@ -93,12 +90,3 @@ export function useApproveCallback( return [approvalState, approve] } - -// wraps useApproveCallback in the context of a swap -export function useApproveCallbackFromTrade(trade?: MobiusTrade, allowedSlippage = 0) { - const amountToApprove = useMemo( - () => (trade ? computeSlippageAdjustedAmounts(trade, allowedSlippage)[Field.INPUT] : undefined), - [trade, allowedSlippage] - ) - return useApproveCallback(amountToApprove, trade?.pool.address) -} diff --git a/src/hooks/useColor.ts b/src/hooks/useColor.ts index 0f2c517f122..d333827ed39 100644 --- a/src/hooks/useColor.ts +++ b/src/hooks/useColor.ts @@ -1,28 +1,15 @@ -import { ChainId, Token } from '@ubeswap/sdk' -import { Coins, STATIC_POOL_INFO } from 'constants/StablePools' +import { Coins, DisplayPool } from 'constants/pools' +import { Token } from 'lib/token-utils' import Vibrant from 'node-vibrant' import { shade } from 'polished' import { useLayoutEffect, useState } from 'react' -import { StablePoolInfo } from 'state/stablePools/hooks' import { useTheme } from 'styled-components' import uriToHttp from 'utils/uriToHttp' import { hex } from 'wcag-contrast' const images: Record = {} -const stablePoolTokens = Object.values(STATIC_POOL_INFO) - .flatMap((pools) => pools) - .flatMap(({ tokens }) => tokens) - -stablePoolTokens.forEach((token) => { - images[token.address] = token.logoURI ?? '' -}) - async function getColorFromToken(token: Token): Promise { - if (token.chainId === ChainId.ALFAJORES && token.address === '0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735') { - return Promise.resolve('#FAAB14') - } - const path = images[token.address] if (!path) { return '#35D07F' @@ -63,7 +50,6 @@ async function getColorFromUriPath(uri: string): Promise { // `radial-gradient(91.85% 100% at 1.84% 0%, ${bgColor1} 0%, ${bgColor2} 100%) `}; export function generateGradient(tokens: Token[]) { - const prevColor = '' let colors = tokens.map((t) => useColor(t)) const numColors = colors.length + 1 const increment = 100 / numColors @@ -74,7 +60,6 @@ export function generateGradient(tokens: Token[]) { } export function generateColorPallete(tokens: Token[]) { - const prevColor = '' let colors = tokens.map((t) => useColor(t)) const increment = 100 / colors.length colors = colors.map((color, i) => `${i * increment}% { background: ${color};}`) @@ -82,9 +67,10 @@ export function generateColorPallete(tokens: Token[]) { return colors.join('\n') } -export function usePoolColor(pool: StablePoolInfo) { +export function usePoolColor(pool: DisplayPool | null) { const theme = useTheme() - const coin = pool.coin + if (pool === null) return null + const coin = pool.peg.coin if (coin === Coins.USD) return theme.cusd if (coin === Coins.Eur) return theme.ceur if (coin === Coins.Ether) return theme.ether diff --git a/src/hooks/useContract.ts b/src/hooks/useContract.ts index 12c5e0eb561..a33ffeb3876 100644 --- a/src/hooks/useContract.ts +++ b/src/hooks/useContract.ts @@ -1,6 +1,6 @@ import { Contract } from '@ethersproject/contracts' import { GOVERNANCE_ADDRESS } from 'constants/governance' -import { GAUGE_CONTROLLER as GAUGE_CONTROLLER_ADDRESS, MOBIUS_MINTER_ADDRESS } from 'constants/StablePools' +import { GAUGE_CONTROLLER as GAUGE_CONTROLLER_ADDRESS, MOBIUS_MINTER_ADDRESS } from 'constants/staking' import { VEMOBI } from 'constants/tokens' import { useMemo } from 'react' diff --git a/src/hooks/useExternalStakingRewards.ts b/src/hooks/useExternalStakingRewards.ts new file mode 100644 index 00000000000..1e3533331c7 --- /dev/null +++ b/src/hooks/useExternalStakingRewards.ts @@ -0,0 +1,52 @@ +import { ExternalRewardsToken } from 'constants/staking' +import JSBI from 'jsbi' +import { Percent, TokenAmount } from 'lib/token-utils' +import { useMobiPrice, useTokenPrice } from 'state/application/hooks' +import { useStakingState, useStakingStateCombined } from 'state/staking/hooks' +import { calcRates } from 'utils/calcRate' + +import { CHAIN } from '../constants' + +const SECONDS_IN_YEAR = JSBI.BigInt(365 * 24 * 60 * 60) +const SECONDS_IN_WEEK = JSBI.BigInt(7 * 24 * 60 * 60) + +export type ExternalRewardInfo = { + rewardRate: TokenAmount + avgApr: Percent +} + +export type ExternalUserRewardInfo = { + userRewardRate: TokenAmount + claimableRewards: TokenAmount +} + +export function useExternalStakingRewards(): ExternalRewardInfo { + const stakingState = useStakingState() + const priceOfReward = useTokenPrice(ExternalRewardsToken[CHAIN].address) + const priceOfMobi = useMobiPrice() + const yearlyRate = JSBI.multiply(stakingState.externalRewardsRate, SECONDS_IN_YEAR) + + const avgApr = + priceOfReward && priceOfMobi + ? calcRates(priceOfReward?.multiply(yearlyRate), priceOfMobi?.multiply(stakingState.totalVotingPower)).apr ?? + new Percent('0') + : new Percent('0') + + return { + rewardRate: new TokenAmount(ExternalRewardsToken[CHAIN], stakingState.externalRewardsRate), + avgApr, + } +} + +export function useUserExternalStakingRewards(): ExternalUserRewardInfo { + const stakingState = useStakingStateCombined() + const userRateJSBI = JSBI.divide( + JSBI.multiply(SECONDS_IN_WEEK, JSBI.multiply(stakingState.externalRewardsRate, stakingState.votingPower)), + stakingState.totalVotingPower + ) + + return { + userRewardRate: new TokenAmount(ExternalRewardsToken[CHAIN], userRateJSBI), + claimableRewards: new TokenAmount(ExternalRewardsToken[CHAIN], stakingState.claimableExternalRewards), + } +} diff --git a/src/hooks/useMento.ts b/src/hooks/useMento.ts deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/hooks/useMentoCallback.ts b/src/hooks/useMentoCallback.ts index 6275b2951a8..bcbc8b819db 100644 --- a/src/hooks/useMentoCallback.ts +++ b/src/hooks/useMentoCallback.ts @@ -1,7 +1,8 @@ import { BigNumber } from '@ethersproject/bignumber' import { Contract } from '@ethersproject/contracts' -import { JSBI, SwapParameters } from '@ubeswap/sdk' +import { SwapParameters } from '@ubeswap/sdk' import { ContractTransaction } from 'ethers' +import JSBI from 'jsbi' import { useMemo } from 'react' import { BIPS_BASE, CHAIN, INITIAL_ALLOWED_SLIPPAGE } from '../constants' @@ -66,7 +67,7 @@ function useSwapCallArguments( args: [ trade.input.raw.toString(), minDy.toString(), - trade.input.currency.address.toLowerCase() === CELO[CHAIN].address.toLowerCase() ? 'true' : '', + trade.input.token.address.toLowerCase() === CELO[CHAIN].address.toLowerCase() ? 'true' : '', ], value: '0', } @@ -171,8 +172,8 @@ export function useSwapCallback( gasLimit: calculateGasMargin(gasEstimate), }) .then((response: ContractTransaction) => { - const inputSymbol = trade.input.currency.symbol - const outputSymbol = trade.output.currency.symbol + const inputSymbol = trade.input.token.symbol + const outputSymbol = trade.output.token.symbol const inputAmount = trade.input.toSignificant(6) const outputAmount = trade.output.toSignificant(6) diff --git a/src/hooks/useStablePools.ts b/src/hooks/useStablePools.ts index e7645f36de4..95a0c5de3ea 100644 --- a/src/hooks/useStablePools.ts +++ b/src/hooks/useStablePools.ts @@ -1,7 +1,62 @@ -import { StablePools } from 'constants/pools' +import { invariant } from '@apollo/client/utilities/globals' +import { IExchangeInfo, IGauge, StablePools } from 'constants/pools' +import JSBI from 'jsbi' +import { calculateVirtualPrice } from 'lib/calculator' +import { Fraction, TokenAmount } from 'lib/token-utils' +import { priceStringToFraction, useTokenPrice, useTokenPrices } from 'state/application/hooks' +import { poolInfoToDisplay, usePools } from 'state/mobiusPools/hooks' import { CHAIN } from '../constants' export const useStablePools = () => { return StablePools[CHAIN] } + +export function useValueOfLp(lpAmount: TokenAmount, exchange: IExchangeInfo): Fraction | undefined { + const display = poolInfoToDisplay(exchange) + const price = useTokenPrice(display.peg.priceQuery ?? undefined) + const virtualPrice = calculateVirtualPrice(exchange) + if (!price || !virtualPrice) return undefined + return lpAmount.multiply(price).multiply(virtualPrice) +} + +export function useValueOfPool(exchange: IExchangeInfo): Fraction | undefined { + return useValueOfLp(exchange.lpTotalSupply, exchange) +} + +export function useValueOfAllPools(): Fraction { + const prices = useTokenPrices() + const pools = usePools() + return StablePools[CHAIN].reduce((acc, cur, i) => { + const price = cur.peg.priceQuery ? priceStringToFraction(prices[cur.peg.priceQuery]) : new Fraction(1) + if (!price) return acc + const virtualPrice = calculateVirtualPrice(pools[i]) + + return virtualPrice + ? acc.add(price.multiply(virtualPrice).multiply(pools[i].lpTotalSupply)) + : acc.add(price).multiply(pools[i].lpTotalSupply) + }, new Fraction(0)) +} + +export function useValueOfAllLP(amounts: JSBI[]): Fraction { + const prices = useTokenPrices() + const pools = usePools() + invariant(amounts.length === pools.length, 'invalid amounts entry') + return StablePools[CHAIN].reduce((acc, cur, i) => { + const price = cur.peg.priceQuery ? priceStringToFraction(prices[cur.peg.priceQuery]) : new Fraction(1) + if (!price) return acc + const virtualPrice = calculateVirtualPrice(pools[i]) + + return virtualPrice ? acc.add(price.multiply(virtualPrice).multiply(amounts[i])) : acc + }, new Fraction(0)) +} + +export function useValueOfExternalRewards(gauge: IGauge | null): Fraction { + const prices = useTokenPrices() + if (!gauge) return new Fraction(0) + return gauge.additionalRewards.reduce((acc, cur) => { + const price = priceStringToFraction(prices[cur.token.address.toLowerCase()]) ?? new Fraction(0) + if (!price) return acc + return acc.add(price.multiply(cur)) + }, new Fraction(0)) +} diff --git a/src/hooks/useStaking.ts b/src/hooks/useStaking.ts new file mode 100644 index 00000000000..53259b6b7a9 --- /dev/null +++ b/src/hooks/useStaking.ts @@ -0,0 +1,6 @@ +import { useUserStakingInfo } from 'state/staking/hooks' + +export function useVotePowerLeft(): number { + const userStakingInfo = useUserStakingInfo() + return (10000 - userStakingInfo.voteUserPower) / 100 +} diff --git a/src/hooks/useSwapCallback.ts b/src/hooks/useSwapCallback.ts index 5ae511c7326..3f77474247c 100644 --- a/src/hooks/useSwapCallback.ts +++ b/src/hooks/useSwapCallback.ts @@ -1,8 +1,10 @@ import { BigNumber } from '@ethersproject/bignumber' import { Contract } from '@ethersproject/contracts' -import { JSBI, SwapParameters } from '@ubeswap/sdk' +import { SwapParameters } from '@ubeswap/sdk' import { ContractTransaction } from 'ethers' +import JSBI from 'jsbi' import { useMemo } from 'react' +import { poolInfoToExchange } from 'state/mobiusPools/hooks' import isZero from 'utils/isZero' import { BIPS_BASE, INITIAL_ALLOWED_SLIPPAGE } from '../constants' @@ -56,12 +58,12 @@ function useSwapCallArguments( return useMemo(() => { if (!trade || !recipient || !provider || !connected || !deadline) return [] - const contract = getStableSwapContract(trade.pool.address, provider, connected) + const contract = getStableSwapContract(poolInfoToExchange(trade.pool).address, provider, connected) const { indexFrom = 0, indexTo = 0 } = trade || {} const outputRaw = trade.output.raw const minDy = JSBI.subtract(outputRaw, JSBI.divide(outputRaw, JSBI.divide(BIPS_BASE, JSBI.BigInt(allowedSlippage)))) const swapCallParameters: SwapParameters = { - methodName: trade.isMeta ? 'swapUnderlying' : 'swap', + methodName: 'swap', args: [ indexFrom.toString(), indexTo.toString(), @@ -133,11 +135,6 @@ export function useSwapCallback( console.debug('Call threw error', call, callError) let errorMessage: string switch (callError.reason) { - case 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT': - case 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT': - errorMessage = - 'This transaction will not succeed either due to price movement or fee on transfer. Try increasing your slippage tolerance.' - break default: errorMessage = `The transaction cannot succeed due to error: ${callError.reason}. This is probably an issue with one of the tokens you are swapping.` } @@ -172,8 +169,8 @@ export function useSwapCallback( gasLimit: gasEstimate.add('100000'), }) .then((response: ContractTransaction) => { - const inputSymbol = trade.input.currency.symbol - const outputSymbol = trade.output.currency.symbol + const inputSymbol = trade.input.token.symbol + const outputSymbol = trade.output.token.symbol const inputAmount = trade.input.toSignificant(6) const outputAmount = trade.output.toSignificant(6) diff --git a/src/hooks/useWarning.ts b/src/hooks/useWarning.ts new file mode 100644 index 00000000000..aa4e05f5b57 --- /dev/null +++ b/src/hooks/useWarning.ts @@ -0,0 +1,12 @@ +import { WarningType } from 'constants/pools' + +import WARNINGS from '../constants/PoolWarnings.json' + +export type WarningModifications = 'require-equal-deposit' | 'none' + +export function useWarning( + warning: WarningType | undefined +): { warning: string; link?: string; modification?: WarningModifications } | undefined { + if (!warning) return undefined + return WARNINGS[warning] as any as { warning: string; link?: string; modification?: WarningModifications } +} diff --git a/src/hooks/web3/web3-context.tsx b/src/hooks/web3/web3-context.tsx index 8fd40eb5518..08917d3324f 100644 --- a/src/hooks/web3/web3-context.tsx +++ b/src/hooks/web3/web3-context.tsx @@ -1,7 +1,6 @@ import { ContractKit } from '@celo/contractkit' import { Connector, useContractKit, useProvider } from '@celo-tools/use-contractkit' import { Web3Provider } from '@ethersproject/providers' -import { useMemo } from 'react' import { CHAIN } from '../../constants' @@ -34,19 +33,16 @@ export const useWeb3Context = () => { connected = true address = uck.address } - return useMemo( - () => ({ - connect: uck.connect, - disconnect: uck.destroy, - kit: uck.kit, - provider, - connected, - address, - chainID, - providerChainID, - }), - [address, chainID, connected, provider, providerChainID, uck.connect, uck.destroy, uck.kit] - ) + return { + connect: uck.connect, + disconnect: uck.destroy, + kit: uck.kit, + provider, + connected, + address, + chainID, + providerChainID, + } } export const useAddress = () => { diff --git a/src/index.tsx b/src/index.tsx index 3a463996afd..fe541d9dc41 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -7,9 +7,10 @@ import React, { StrictMode } from 'react' import ReactDOM from 'react-dom' import { Provider } from 'react-redux' import { HashRouter } from 'react-router-dom' +import { GaugeUpdater } from 'state/gauges/updater' import { UpdateMento } from 'state/mentoPools/updater' +import { UpdatePools } from 'state/mobiusPools/updater' import { UpdateOpenSum } from 'state/openSum/updater' -import { BatchUpdateGauges, UpdateVariablePoolInfo } from 'state/stablePools/updater' import StakingUpdater from 'state/staking/updater' import mobiusIcon from './assets/svg/mobius.svg' @@ -38,8 +39,10 @@ function Updaters() { - - + {/* */} + + {/* */} + diff --git a/src/utils/calculator/amounts.ts b/src/lib/calculator/amounts.ts similarity index 78% rename from src/utils/calculator/amounts.ts rename to src/lib/calculator/amounts.ts index 85e164ba2c7..e0ceb12bdb2 100644 --- a/src/utils/calculator/amounts.ts +++ b/src/lib/calculator/amounts.ts @@ -1,10 +1,9 @@ -import type { Token } from '@saberhq/token-utils' -import { Fraction, ONE, TokenAmount, ZERO } from '@saberhq/token-utils' +import type { Fees, IExchangeInfo } from 'constants/pools' import JSBI from 'jsbi' +import type { Token } from 'lib/token-utils' +import { Fraction, ONE, TokenAmount, ZERO } from 'lib/token-utils' import mapValues from 'lodash.mapvalues' -import type { IExchangeInfo } from '../entities/exchange' -import type { Fees } from '../state/fees' import { computeD, computeY } from './curve' /** @@ -19,7 +18,7 @@ export const calculateVirtualPrice = (exchange: IExchangeInfo): Fraction | null return null } const price = new Fraction( - computeD(exchange.ampFactor, exchange.reserves[0].amount.raw, exchange.reserves[1].amount.raw), + computeD(exchange.ampFactor, exchange.reserves[0].raw, exchange.reserves[1].raw), amount.raw ) return price @@ -37,12 +36,12 @@ export const calculateEstimatedSwapOutputAmount = ( ): { [K in 'outputAmountBeforeFees' | 'outputAmount' | 'fee' | 'lpFee' | 'adminFee']: TokenAmount } => { - const [fromReserves, toReserves] = fromAmount.token.equals(exchange.reserves[0].amount.token) + const [fromReserves, toReserves] = fromAmount.token.equals(exchange.reserves[0].token) ? [exchange.reserves[0], exchange.reserves[1]] : [exchange.reserves[1], exchange.reserves[0]] if (fromAmount.equalTo(0)) { - const zero = new TokenAmount(toReserves.amount.token, ZERO) + const zero = new TokenAmount(toReserves.token, ZERO) return { outputAmountBeforeFees: zero, outputAmount: zero, @@ -55,28 +54,18 @@ export const calculateEstimatedSwapOutputAmount = ( const amp = exchange.ampFactor const amountBeforeFees = JSBI.subtract( - toReserves.amount.raw, - computeY( - amp, - JSBI.add(fromReserves.amount.raw, fromAmount.raw), - computeD(amp, fromReserves.amount.raw, toReserves.amount.raw) - ) + toReserves.raw, + computeY(amp, JSBI.add(fromReserves.raw, fromAmount.raw), computeD(amp, fromReserves.raw, toReserves.raw)) ) - const outputAmountBeforeFees = new TokenAmount(toReserves.amount.token, amountBeforeFees) + const outputAmountBeforeFees = new TokenAmount(toReserves.token, amountBeforeFees) - const fee = new TokenAmount( - toReserves.amount.token, - exchange.fees.trade.asFraction.multiply(amountBeforeFees).toFixed(0) - ) + const fee = new TokenAmount(toReserves.token, exchange.fees.trade.asFraction.multiply(amountBeforeFees).toFixed(0)) - const adminFee = new TokenAmount( - toReserves.amount.token, - exchange.fees.adminTrade.asFraction.multiply(fee.raw).toFixed(0) - ) + const adminFee = new TokenAmount(toReserves.token, exchange.fees.admin.asFraction.multiply(fee.raw).toFixed(0)) const lpFee = fee.subtract(adminFee) - const outputAmount = new TokenAmount(toReserves.amount.token, JSBI.subtract(amountBeforeFees, fee.raw)) + const outputAmount = new TokenAmount(toReserves.token, JSBI.subtract(amountBeforeFees, fee.raw)) return { outputAmountBeforeFees, @@ -95,9 +84,7 @@ export interface IWithdrawOneResult { swapFee: TokenAmount withdrawFee: TokenAmount lpSwapFee: TokenAmount - lpWithdrawFee: TokenAmount adminSwapFee: TokenAmount - adminWithdrawFee: TokenAmount } /** @@ -121,9 +108,7 @@ export const calculateEstimatedWithdrawOneAmount = ({ swapFee: ZERO, withdrawFee: ZERO, lpSwapFee: ZERO, - lpWithdrawFee: ZERO, adminSwapFee: ZERO, - adminWithdrawFee: ZERO, } return mapValues(quantities, (q) => new TokenAmount(withdrawToken, q)) } @@ -131,8 +116,8 @@ export const calculateEstimatedWithdrawOneAmount = ({ const { ampFactor, fees } = exchange const [baseReserves, quoteReserves] = [ - exchange.reserves.find((r) => r.amount.token.equals(withdrawToken))?.amount.raw ?? ZERO, - exchange.reserves.find((r) => !r.amount.token.equals(withdrawToken))?.amount.raw ?? ZERO, + exchange.reserves.find((r) => r.token.equals(withdrawToken))?.raw ?? ZERO, + exchange.reserves.find((r) => !r.token.equals(withdrawToken))?.raw ?? ZERO, ] const d_0 = computeD(ampFactor, baseReserves, quoteReserves) @@ -160,12 +145,10 @@ export const calculateEstimatedWithdrawOneAmount = ({ const withdrawFee = dy.multiply(fees.withdraw.asFraction) // admin fees - const adminSwapFee = swapFee.multiply(fees.adminTrade.asFraction) - const adminWithdrawFee = withdrawFee.multiply(fees.adminWithdraw.asFraction) + const adminSwapFee = swapFee.multiply(fees.admin.asFraction) // final LP fees const lpSwapFee = swapFee.subtract(adminSwapFee) - const lpWithdrawFee = withdrawFee.subtract(adminWithdrawFee) // final withdraw amount const withdrawAmount = dy.subtract(withdrawFee).subtract(swapFee) @@ -177,9 +160,7 @@ export const calculateEstimatedWithdrawOneAmount = ({ swapFee, withdrawFee, lpSwapFee, - lpWithdrawFee, adminSwapFee, - adminWithdrawFee, } return mapValues(quantities, (q) => new TokenAmount(withdrawToken, q.toFixed(0))) @@ -209,7 +190,7 @@ export const calculateEstimatedWithdrawAmount = ({ fees: readonly [TokenAmount, TokenAmount] } => { if (lpTotalSupply.equalTo(0)) { - const zero = reserves.map((r) => new TokenAmount(r.amount.token, ZERO)) as [TokenAmount, TokenAmount] + const zero = reserves.map((r) => new TokenAmount(r.token, ZERO)) as [TokenAmount, TokenAmount] return { withdrawAmounts: zero, withdrawAmountsBeforeFees: zero, @@ -219,7 +200,7 @@ export const calculateEstimatedWithdrawAmount = ({ const share = poolTokenAmount.divide(lpTotalSupply) - const withdrawAmounts = reserves.map(({ amount }) => { + const withdrawAmounts = reserves.map((amount) => { const baseAmount = share.multiply(amount.raw) const fee = baseAmount.multiply(fees.withdraw.asFraction) return [ @@ -267,18 +248,15 @@ export const calculateEstimatedMintAmount = ( const amp = exchange.ampFactor const [reserveA, reserveB] = exchange.reserves - const d0 = computeD(amp, reserveA.amount.raw, reserveB.amount.raw) + const d0 = computeD(amp, reserveA.raw, reserveB.raw) - const d1 = computeD(amp, JSBI.add(reserveA.amount.raw, depositAmountA), JSBI.add(reserveB.amount.raw, depositAmountB)) + const d1 = computeD(amp, JSBI.add(reserveA.raw, depositAmountA), JSBI.add(reserveB.raw, depositAmountB)) if (JSBI.lessThan(d1, d0)) { throw new Error('New D cannot be less than previous D') } - const oldBalances = exchange.reserves.map((r) => r.amount.raw) as [JSBI, JSBI] - const newBalances = [ - JSBI.add(reserveA.amount.raw, depositAmountA), - JSBI.add(reserveB.amount.raw, depositAmountB), - ] as const + const oldBalances = exchange.reserves.map((r) => r.raw) as [JSBI, JSBI] + const newBalances = [JSBI.add(reserveA.raw, depositAmountA), JSBI.add(reserveB.raw, depositAmountB)] as const const adjustedBalances = newBalances.map((newBalance, i) => { const oldBalance = oldBalances[i] as JSBI const idealBalance = new Fraction(d1, d0).multiply(oldBalance) diff --git a/src/utils/calculator/curve.ts b/src/lib/calculator/curve.ts similarity index 98% rename from src/utils/calculator/curve.ts rename to src/lib/calculator/curve.ts index 54eb9a8849e..4f3e07ca97a 100644 --- a/src/utils/calculator/curve.ts +++ b/src/lib/calculator/curve.ts @@ -1,5 +1,5 @@ -import { ONE, ZERO } from '@saberhq/token-utils' import JSBI from 'jsbi' +import { ONE, ZERO } from 'lib/token-utils' const N_COINS = JSBI.BigInt(2) // n diff --git a/src/utils/calculator/index.ts b/src/lib/calculator/index.ts similarity index 100% rename from src/utils/calculator/index.ts rename to src/lib/calculator/index.ts diff --git a/src/utils/calculator/price.ts b/src/lib/calculator/price.ts similarity index 55% rename from src/utils/calculator/price.ts rename to src/lib/calculator/price.ts index d634789865f..c7e745976e5 100644 --- a/src/utils/calculator/price.ts +++ b/src/lib/calculator/price.ts @@ -1,8 +1,16 @@ -import { Price, TokenAmount } from '@saberhq/token-utils' -import BN from 'bn.js' +import type { IExchangeInfo } from 'constants/pools' +import JSBI from 'jsbi' +import { Price, TEN, TokenAmount } from 'lib/token-utils' -import type { IExchangeInfo } from '..' -import { calculateEstimatedSwapOutputAmount } from '..' +import { calculateEstimatedSwapOutputAmount } from './' + +function min(a: JSBI, b: JSBI): JSBI { + return JSBI.greaterThanOrEqual(a, b) ? b : a +} + +function max(a: JSBI, b: JSBI): JSBI { + return JSBI.greaterThanOrEqual(a, b) ? a : b +} /** * Gets the price of the second token in the swap, i.e. "Token 1", with respect to "Token 0". @@ -11,15 +19,15 @@ import { calculateEstimatedSwapOutputAmount } from '..' * @returns */ export const calculateSwapPrice = (exchangeInfo: IExchangeInfo): Price => { - const reserve0 = exchangeInfo.reserves[0].amount - const reserve1 = exchangeInfo.reserves[1].amount + const reserve0 = exchangeInfo.reserves[0] + const reserve1 = exchangeInfo.reserves[1] // We try to get at least 4 decimal points of precision here // Otherwise, we attempt to swap 1% of total supply of the pool // or at most, $1 - const inputAmountNum = Math.max( - 10_000, - Math.min(10 ** reserve0.token.decimals, Math.floor(parseInt(reserve0.toU64().div(new BN(100)).toString()))) + const inputAmountNum = max( + JSBI.BigInt(10_000), + min(JSBI.exponentiate(TEN, JSBI.BigInt(reserve0.token.decimals)), reserve0.divide(100).quotient) ) const inputAmount = new TokenAmount(reserve0.token, inputAmountNum) diff --git a/src/utils/mentoCalculator/amounts.ts b/src/lib/mentoCalculator/amounts.ts similarity index 98% rename from src/utils/mentoCalculator/amounts.ts rename to src/lib/mentoCalculator/amounts.ts index 4e09d5e3ae0..b6f65b1da87 100644 --- a/src/utils/mentoCalculator/amounts.ts +++ b/src/lib/mentoCalculator/amounts.ts @@ -1,5 +1,5 @@ -import { TokenAmount } from '@ubeswap/sdk' import JSBI from 'jsbi' +import { TokenAmount } from 'lib/token-utils' import { BIPS_BASE } from '../../constants' import type { IMentoExchangeInfo } from '../../constants/mento' diff --git a/src/utils/mentoCalculator/index.ts b/src/lib/mentoCalculator/index.ts similarity index 100% rename from src/utils/mentoCalculator/index.ts rename to src/lib/mentoCalculator/index.ts diff --git a/src/utils/mentoCalculator/price.ts b/src/lib/mentoCalculator/price.ts similarity index 92% rename from src/utils/mentoCalculator/price.ts rename to src/lib/mentoCalculator/price.ts index 0a2f76d1e8d..848dec5ae65 100644 --- a/src/utils/mentoCalculator/price.ts +++ b/src/lib/mentoCalculator/price.ts @@ -1,4 +1,4 @@ -import { Price } from '@ubeswap/sdk' +import { Price } from 'lib/token-utils' import type { IMentoExchangeInfo } from '../../constants/mento' diff --git a/src/lib/token-utils/index.ts b/src/lib/token-utils/index.ts new file mode 100644 index 00000000000..c619a621264 --- /dev/null +++ b/src/lib/token-utils/index.ts @@ -0,0 +1,23 @@ +export * from './price' +export * from './splTokenRegistry' +export * from './token' +export * from './tokenAmount' +export * from './tokenList' + +// re-export token-math types +// so consumers don't need to use them +export type { BigintIsh, IFormatUint, NumberFormat } from '@ubeswap/token-math' +export { + Fraction, + makeDecimalMultiplier, + MAX_U64, + MAX_U256, + ONE, + parseBigintIsh, + Percent, + Rounding, + TEN, + validateU64, + validateU256, + ZERO, +} from '@ubeswap/token-math' diff --git a/src/lib/token-utils/price.ts b/src/lib/token-utils/price.ts new file mode 100644 index 00000000000..190285ed919 --- /dev/null +++ b/src/lib/token-utils/price.ts @@ -0,0 +1,44 @@ +import type { BigintIsh } from '@ubeswap/token-math' +import { Price as UPrice } from '@ubeswap/token-math' +import invariant from 'tiny-invariant' + +import type { Token } from './token' +import { TokenAmount } from './tokenAmount' + +/** + * A price of one token relative to another. + */ +export class Price extends UPrice { + /** + * Constructs a price. + * @param baseCurrency + * @param quoteCurrency + * @param denominator + * @param numerator + */ + constructor(baseCurrency: Token, quoteCurrency: Token, denominator: BigintIsh, numerator: BigintIsh) { + super(baseCurrency, quoteCurrency, denominator, numerator) + } + + override invert(): Price { + return new Price(this.quoteCurrency, this.baseCurrency, this.numerator, this.denominator) + } + + override multiply(other: Price): Price { + invariant( + this.quoteCurrency.equals(other.baseCurrency), + `multiply token mismatch: ${this.quoteCurrency.toString()} !== ${other.baseCurrency.toString()}` + ) + const fraction = super.asFraction.multiply(other) + return new Price(this.baseCurrency, other.quoteCurrency, fraction.denominator, fraction.numerator) + } + + override quote(tokenAmount: TokenAmount): TokenAmount { + const amt = super.quote(tokenAmount) + return new TokenAmount(this.quoteCurrency, amt.raw) + } + + static fromUPrice(price: UPrice): Price { + return new Price(price.baseCurrency, price.quoteCurrency, price.denominator, price.numerator) + } +} diff --git a/src/lib/token-utils/splTokenRegistry.ts b/src/lib/token-utils/splTokenRegistry.ts new file mode 100644 index 00000000000..c20ee8d8eba --- /dev/null +++ b/src/lib/token-utils/splTokenRegistry.ts @@ -0,0 +1,107 @@ +/** + * These types all come from the @solana/spl-token-registry package. + * + * We re-export them here so we do not have to have a hard dependency on + * that package, which is massive. + */ + +export enum ChainId { + Mainnet = 42220, + Alfajores = 44787, + Baklava = 62320, +} +export enum TradeType { + EXACT_INPUT = 0, + EXACT_OUTPUT = 1, +} + +export enum NetworkNames { + Alfajores = 'Alfajores', + Baklava = 'Baklava', + Mainnet = 'Mainnet', +} + +export interface Network { + name: NetworkNames + rpcUrl: string + graphQl: string + explorer: string + chainId: ChainId +} + +export const Alfajores: Network = { + name: NetworkNames.Alfajores, + rpcUrl: 'https://alfajores-forno.celo-testnet.org', + graphQl: 'https://alfajores-blockscout.celo-testnet.org/graphiql', + explorer: 'https://alfajores-blockscout.celo-testnet.org', + chainId: ChainId.Alfajores, +} +export const Baklava: Network = { + name: NetworkNames.Baklava, + rpcUrl: 'https://baklava-forno.celo-testnet.org', + graphQl: 'https://baklava-blockscout.celo-testnet.org/graphiql', + explorer: 'https://baklava-blockscout.celo-testnet.org', + chainId: ChainId.Baklava, +} +export const Mainnet: Network = { + name: NetworkNames.Mainnet, + rpcUrl: 'https://forno.celo.org', + graphQl: 'https://explorer.celo.org/graphiql', + explorer: 'https://explorer.celo.org', + chainId: ChainId.Mainnet, +} + +/** + * A token list. + */ +export interface SPLTokenList { + readonly name: string + readonly logoURI: string + readonly tags: { [tag: string]: TagDetails } + readonly timestamp: string + readonly tokens: SPLTokenInfo[] +} + +/** + * Tag details. + */ +export interface TagDetails { + readonly name: string + readonly description: string +} + +/** + * TokenExtensions. + */ +export interface SPLTokenExtensions { + readonly website?: string + readonly bridgeContract?: string + readonly assetContract?: string + readonly address?: string + readonly explorer?: string + readonly twitter?: string + readonly github?: string + readonly medium?: string + readonly tgann?: string + readonly tggroup?: string + readonly discord?: string + readonly serumV3Usdt?: string + readonly serumV3Usdc?: string + readonly coingeckoId?: string + readonly imageUrl?: string + readonly description?: string +} + +/** + * TokenInfo. + */ +export interface SPLTokenInfo { + readonly chainId: number + readonly address: string + readonly name: string + readonly decimals: number + readonly symbol: string + readonly logoURI?: string + readonly tags?: string[] + readonly extensions?: SPLTokenExtensions +} diff --git a/src/lib/token-utils/token.ts b/src/lib/token-utils/token.ts new file mode 100644 index 00000000000..981164a752b --- /dev/null +++ b/src/lib/token-utils/token.ts @@ -0,0 +1,108 @@ +import type { Token as UToken } from '@ubeswap/token-math' + +import { Alfajores, Baklava, ChainId, Mainnet, Network } from './' +import type { TokenInfo } from './tokenList' + +/** + * Token information. + */ +export class Token implements UToken { + /** + * The network that the Token is on. + */ + readonly network: Network + + constructor(readonly info: TokenInfo) { + this.network = chainIdToNetwork(info.chainId) ?? Mainnet + } + + /** + * The Base58 string representation of the mint address. + */ + get address(): string { + return this.info.address + } + + /** + * The chain ID of the token. + */ + get chainId(): number { + return this.info.chainId + } + + /** + * Number of decimals of the token. + */ + get decimals(): number { + return this.info.decimals + } + + /** + * The name of the token. + */ + get name(): string { + return this.info.name + } + + /** + * The symbol of the token. + */ + get symbol(): string { + return this.info.symbol + } + + /** + * The token's icon to render. + */ + get icon(): string | undefined { + return this.info.logoURI + } + + equals(other: Token): boolean { + return tokensEqual(this, other) + } + + toString(): string { + return `Token[address=${this.address}, decimals=${this.decimals}, network=${this.network}]` + } + + toJSON(): unknown { + return this.info + } + + /** + * Returns true if the given tag is present. + * @param tag The tag to check. + * @returns + */ + hasTag(tag: string): boolean { + return !!this.info.tags?.includes(tag) + } +} + +/** + * Checks if two tokens are equal. + * @param a + * @param b + * @returns + */ +export const tokensEqual = (a: Token | undefined, b: Token | undefined): boolean => + a !== undefined && b !== undefined && a.address === b.address && a.network === b.network + +/** + * Map of network to Token + */ +export type TokenMap = { [c in ChainId]: Token } + +export const CHAIN_ID_TO_NETWORK = { + [ChainId.Mainnet]: Mainnet, + [ChainId.Alfajores]: Alfajores, + [ChainId.Baklava]: Baklava, +} + +/** + * Gets the Network associated with a chain id. + * @param network + * @returns + */ +export const chainIdToNetwork = (env: ChainId): Network => CHAIN_ID_TO_NETWORK[env] diff --git a/src/lib/token-utils/tokenAmount.ts b/src/lib/token-utils/tokenAmount.ts new file mode 100644 index 00000000000..e271631bd6c --- /dev/null +++ b/src/lib/token-utils/tokenAmount.ts @@ -0,0 +1,89 @@ +import type { BigintIsh, NumberFormat, Percent } from '@ubeswap/token-math' +import { TokenAmount as UTokenAmount } from '@ubeswap/token-math' + +import type { Token } from './token' + +export interface IFormatUint { + /** + * If specified, format this according to `toLocaleString` + */ + numberFormatOptions?: Intl.NumberFormatOptions + /** + * Locale of the number + */ + locale?: string +} + +export class TokenAmount extends UTokenAmount { + // amount _must_ be raw, i.e. in the native representation + constructor(token: Token, amount: BigintIsh) { + super(token, amount) + } + + /** + * Parses a token amount from a decimal representation. + * @param token + * @param uiAmount + * @returns + */ + static parse(token: Token, uiAmount: string): TokenAmount { + const prev = UTokenAmount.parseFromString(token, uiAmount) + return new TokenAmount(token, prev.raw) + } + + override add(other: TokenAmount): TokenAmount { + const result = super.add(other) + return new TokenAmount(this.token, result.raw) + } + override subtract(other: TokenAmount): TokenAmount { + const result = super.subtract(other) + return new TokenAmount(this.token, result.raw) + } + override multiplyBy(percent: Percent): TokenAmount { + const result = super.multiplyBy(percent) + return new TokenAmount(this.token, result.raw) + } + override reduceBy(percent: Percent): TokenAmount { + const result = super.reduceBy(percent) + return new TokenAmount(this.token, result.raw) + } + + /** + * Formats the token amount with units and decimal adjustment, e.g. "100.42 SOL" + * @returns + */ + formatUnits(format: NumberFormat = { groupSeparator: ',' }): string { + return `${this.toExact(format)} ${this.token.symbol}` + } + + /** + * String representation of this token amount. + */ + override toString(): string { + return `TokenAmount[Token=(${this.token.toString()}), amount=${this.toExact()}` + } + + /** + * JSON representation of the token amount. + */ + toJSON(): { + /** + * Discriminator to show this is a token amount. + */ + _isTA: true + /** + * Mint of the token. + */ + mint: string + /** + * Amount of tokens in string representation. + */ + uiAmount: string + } { + return { + _isTA: true, + mint: this.token.address, + uiAmount: this.toExact(), + } + } +} diff --git a/src/lib/token-utils/tokenList.ts b/src/lib/token-utils/tokenList.ts new file mode 100644 index 00000000000..97350e9e712 --- /dev/null +++ b/src/lib/token-utils/tokenList.ts @@ -0,0 +1,97 @@ +import type { SPLTokenExtensions, SPLTokenInfo, SPLTokenList } from './splTokenRegistry' +import { Token } from './token' + +/** + * Known origin chains. + */ +export const ORIGIN_CHAINS = [ + 'bitcoin', + 'ethereum', + 'terra', + 'avalanche', + 'binance', + 'celo', + 'polygon', + 'fantom', + 'polygon', + 'heco', +] as const + +/** + * Known origin chains. + */ +export type OriginChain = typeof ORIGIN_CHAINS[number] + +/** + * Token extensions with additional information. + */ +export type TokenExtensions = SPLTokenExtensions & { + /** + * Mints of the underlying tokens that make up this token. + * E.g. a Saber USDC-USDT LP token would use the USDC and USDT mints. + */ + readonly underlyingTokens?: string[] + /** + * The protocol that this token comes from. + * E.g. `wormhole-v1`, `wormhole-v2`, `allbridge`, `sollet`, `saber`. + */ + readonly source?: string + + /* + ** Link to the source's website where you can acquire this token + */ + readonly sourceUrl?: string + /** + * The currency code of what this token represents, e.g. BTC, ETH, USD. + */ + readonly currency?: string + /** + * If this token is a bridged token, this is the chain that the asset originates from. + */ + readonly originChain?: OriginChain +} + +/** + * Token info. + */ +export type TokenInfo = Omit & { + readonly extensions?: TokenExtensions +} + +/** + * A list of tokens, based off of the Uniswap standard. + */ +export type TokenList = Omit & { + readonly tokens: TokenInfo[] +} + +/** + * Creates a token map from a TokenList. + * @param tokens + * @returns + */ +export const makeTokenMap = (tokenList: TokenList): Record => { + const ret: Record = {} + tokenList.tokens.forEach((item) => { + ret[item.address] = new Token(item) + }) + return ret +} + +/** + * Dedupes a list of tokens, picking the first instance of the token in a list. + * @param tokens + * @returns + */ +export const dedupeTokens = (tokens: TokenInfo[]): TokenInfo[] => { + const seen = new Set() + return tokens.filter((token) => { + const tokenID = `${token.address}_${token.chainId}` + if (seen.has(tokenID)) { + return false + } else { + seen.add(tokenID) + return true + } + }) +} diff --git a/src/pages/App.tsx b/src/pages/App.tsx index c0834443bd8..a09777cd5b5 100644 --- a/src/pages/App.tsx +++ b/src/pages/App.tsx @@ -23,7 +23,6 @@ import Reset from './Reset' import RiskPage from './Risk' import Staking from './Staking' import Swap from './Swap' -import { RedirectToSwap } from './Swap/redirects' import Vote from './Vote' import VotePage from './Vote/VotePage' @@ -45,7 +44,7 @@ const BodyWrapper = styled.div` display: flex; flex-direction: column; width: 100%; - ${({ giveSpace }) => giveSpace && `padding-top: 100px;`} + padding-top: 100px; align-items: center; flex: 1; overflow-y: auto; @@ -81,6 +80,7 @@ export default function App() { if (params.get('status') === DappKitResponseStatus.SUCCESS) { localStorage.setItem(localStorageKey, window.location.href) const mobileOS = getMobileOperatingSystem() + // TODO: test the effect of this with h if (mobileOS === Mobile.ANDROID) { window.close() } @@ -94,7 +94,7 @@ export default function App() { return ( - + {location.pathname !== '/' && ( <> @@ -122,17 +122,15 @@ export default function App() { - - {' '} + {' '} - {/* */} {location.pathname !== '/' && } diff --git a/src/pages/Burn/index.tsx b/src/pages/Burn/index.tsx index e69c2c68979..f7589c9a9fe 100644 --- a/src/pages/Burn/index.tsx +++ b/src/pages/Burn/index.tsx @@ -1,28 +1,29 @@ import { TransactionResponse } from '@ethersproject/providers' import { ButtonConfirmed } from 'components/Button' import { AutoColumn } from 'components/Column' -import { STATIC_POOL_INFO } from 'constants/StablePools' -import { useWeb3Context } from 'hooks' +import { StablePools } from 'constants/pools' +import { useSwappableTokens } from 'hooks/Tokens' import { useFeeDistributor, usePoolProxy } from 'hooks/useContract' import React, { useState } from 'react' import { isMobile } from 'react-device-detect' -import { useStablePoolInfo } from 'state/stablePools/hooks' import { useTransactionAdder } from 'state/transactions/hooks' import styled from 'styled-components' import { TYPE } from 'theme' +import { CHAIN } from '../../constants' + const PageWrapper = styled(AutoColumn)` max-width: 640px; width: 100%; ` const blackList: Set = new Set([ - '0xcC82628f6A8dEFA1e2B0aD7ed448bef3647F7941', - '0xBe50a3013A1c94768A1ABb78c3cB79AB28fc1aCE', - '0xE919F65739c26a42616b7b8eedC6b5524d1e3aC4', - '0xcFFfE0c89a779c09Df3DF5624f54cDf7EF5fDd5D', - '0x93DB49bE12B864019dA9Cb147ba75cDC0506190e', - '0xd7Bf6946b740930c60131044bD2F08787e1DdBd4', + '0xcC82628f6A8dEFA1e2B0aD7ed448bef3647F7941'.toLowerCase(), + '0xBe50a3013A1c94768A1ABb78c3cB79AB28fc1aCE'.toLowerCase(), + '0xE919F65739c26a42616b7b8eedC6b5524d1e3aC4'.toLowerCase(), + '0xcFFfE0c89a779c09Df3DF5624f54cDf7EF5fDd5D'.toLowerCase(), + '0x93DB49bE12B864019dA9Cb147ba75cDC0506190e'.toLowerCase(), + '0xd7Bf6946b740930c60131044bD2F08787e1DdBd4'.toLowerCase(), ]) export default function BurnPage() { @@ -30,20 +31,15 @@ export default function BurnPage() { const poolProxy = usePoolProxy() const feeDistributor = useFeeDistributor() const addTransaction = useTransactionAdder() - const { chainID } = useWeb3Context() - const tokenSeen: Set = new Set() - const tokens: string[] = STATIC_POOL_INFO[chainID ?? chainID.MAINNET] - .flatMap((el) => el.tokens.map((tok) => tok.address)) - .filter((el) => { - if (tokenSeen.has(el)) return false - tokenSeen.add(el) - return !blackList.has(el) - }) - const pools = useStablePoolInfo() + const tokens = useSwappableTokens(false) + .filter((el) => !blackList.has(el.address.toLowerCase())) + .map(({ address }) => address) + + const pools = StablePools[CHAIN] const initProcess = async () => { for (let i = 0; i < pools.length; i += 20) { - const addresses = pools.slice(i, Math.min(i + 20, pools.length)).map((p) => p.poolAddress) + const addresses = pools.slice(i, Math.min(i + 20, pools.length)).map((p) => p.pool.address) await poolProxy ?.withdraw_many(addresses, { gasLimit: 10000000 }) .then((response: TransactionResponse) => { diff --git a/src/pages/Charts/index.tsx b/src/pages/Charts/index.tsx index f349470db2c..cc1a19a28ca 100644 --- a/src/pages/Charts/index.tsx +++ b/src/pages/Charts/index.tsx @@ -5,38 +5,19 @@ import Logo from 'components/Logo' import Row, { RowBetween, RowFixed } from 'components/Row' import Toggle from 'components/Toggle' import VolumeChart from 'components/VolumeChart' -import { ChainLogo, Coins, PRICE } from 'constants/StablePools' +import { ChainLogo, StablePools } from 'constants/pools' import useTheme from 'hooks/useTheme' import { useWindowSize } from 'hooks/useWindowSize' +import { Fraction } from 'lib/token-utils' import React, { useState } from 'react' import { isMobile } from 'react-device-detect' -import { usePools } from 'state/stablePools/hooks' -import { StableSwapPool } from 'state/stablePools/reducer' +import { useTokenPrices } from 'state/application/hooks' +import { getCurrentDisplayAddress, getCurrentExchangeAddress } from 'state/mobiusPools/hooks' import styled from 'styled-components' import { Sel, TYPE } from 'theme' +import invariant from 'tiny-invariant' -const TextContainer = styled.div` - width: 100%; - display: flex; - flex-wrap: wrap; - margin-left: auto; - margin-right: auto; - justify-content: center; -` - -const PositionsContainer = styled.div` - width: 100%; - display: flex; - justify-content: space-between; - flex-wrap: wrap; - display: flex; - align-items: flex-start; - justify-content: center; - ${({ theme }) => theme.mediaWidth.upToSmall` - width: 100%; - `} - flex-wrap: wrap; -` +import { CHAIN } from '../../constants' const OuterContainer = styled.div` width: min(1280px, 100%); @@ -117,36 +98,33 @@ const granularityMapping: { [g in Granularity]: string } = { [Granularity.Week]: 'weeklyVolumes', } -const timeFormat: { [g: Granularity]: (n: number) => string } = { +const timeFormat: { [g in Granularity]: (n: number) => string } = { [Granularity.Hour]: (t: number) => new Date(t * 1000).toLocaleTimeString(), [Granularity.Day]: (t: number) => new Date(t * 1000).toLocaleDateString(), [Granularity.Week]: (t: number) => new Date(t * 1000).toLocaleDateString(), } -function getPoolName(pools: StableSwapPool[], address: string) { - return pools.filter((p) => p.address.toLowerCase() == address)[0]?.name ?? 'Unknown' -} - export default function Charts() { const { data, loading, error } = useQuery(volumeQuery) const [granularity, setGranularity] = useState(Granularity.Week) const [showTotal, setShowTotal] = useState(true) - const [selectedPools, setSelectedPools] = useState>({}) + const [selectedPools, setSelectedPools] = useState>({}) const [showPoolSelect, setShowPoolSelect] = useState(false) - const pools = usePools().slice() + const displayPools = StablePools[CHAIN] const { width } = useWindowSize() const theme = useTheme() + const prices = useTokenPrices() const totals = data - ? data.swaps.reduce((accum, info) => { - const price = - info.id === '0x19260b9b573569dDB105780176547875fE9fedA3'.toLowerCase() - ? PRICE[Coins.Bitcoin] - : info.id === '0xE0F2cc70E52f05eDb383313393d88Df2937DA55a'.toLowerCase() - ? PRICE[Coins.Ether] - : PRICE[Coins.USD] - info[granularityMapping[granularity]].forEach((vol, i) => { - accum[vol.timestamp] = price * parseInt(vol.volume) + (accum[vol.timestamp] ?? 0) + ? data.swaps.reduce((accum: { [timestamp: string]: number }, info: any) => { + const exchange = getCurrentDisplayAddress(info.id) + if (exchange === null) return accum + const price = exchange.peg.priceQuery !== null ? prices[exchange.peg.priceQuery] : new Fraction(1) + console.log(price.valueOf()) + + info[granularityMapping[granularity]].forEach((vol: any) => { + console.log(vol) + accum[vol.timestamp] = parseInt(price.toString()) * parseInt(vol.volume) + (accum[vol.timestamp] ?? 0) }) return accum }, {}) @@ -154,15 +132,16 @@ export default function Charts() { let dataAndLabels = data ? data.swaps - .filter(({ id }: { id: string }) => selectedPools[id]) - .sort((p1, p2) => { + .filter(({ id }: { id: string }) => selectedPools[id] !== null) + .sort((p1: any, p2: any) => { + invariant(selectedPools[p1.id] !== null && selectedPools[p2.id] !== null) if (selectedPools[p1.id] < selectedPools[p2.id]) return 1 if (selectedPools[p1.id] > selectedPools[p2.id]) return -1 return 0 }) - .map((info) => [ - getPoolName(pools, info.id), - info[granularityMapping[granularity]].map((vol) => ({ + .map((info: any) => [ + getCurrentExchangeAddress(info.id), + info[granularityMapping[granularity]].map((vol: any) => ({ x: parseInt(vol.timestamp), y: parseInt(vol.volume), })), @@ -172,8 +151,8 @@ export default function Charts() { dataAndLabels.push(['Total', Object.entries(totals).map(([time, vol]) => ({ x: time, y: vol }))]) } dataAndLabels = dataAndLabels.reverse() - const chartData = dataAndLabels.map((group) => group[1]) - const labels = dataAndLabels.map((group) => group[0]) + const chartData = dataAndLabels.map((group: any) => group[1]) + const labels = dataAndLabels.map((group: any) => group[0]) return ( @@ -210,24 +189,23 @@ export default function Charts() { setShowTotal(!showTotal)} /> - {pools - .sort((p1, p2) => p1.displayChain - p2.displayChain) - .filter(({ disabled }) => !disabled) + {displayPools + .sort((p1, p2) => p1.chain - p2.chain) .map((p) => ( - {' '} + {' '} {p.name} { - if (selectedPools[p.address.toLowerCase()]) { - setSelectedPools({ ...selectedPools, [p.address.toLowerCase()]: undefined }) + if (selectedPools[p.pool.address.toLowerCase()]) { + setSelectedPools({ ...selectedPools, [p.pool.address.toLowerCase()]: null }) } else { setSelectedPools({ ...selectedPools, - [p.address.toLowerCase()]: Object.keys(selectedPools).length + 1, + [p.pool.address.toLowerCase()]: Object.keys(selectedPools).length + 1, }) } }} @@ -242,7 +220,7 @@ export default function Charts() { diff --git a/src/pages/Claim/index.tsx b/src/pages/Claim/index.tsx index 41fc1b32b84..b763736bd8b 100644 --- a/src/pages/Claim/index.tsx +++ b/src/pages/Claim/index.tsx @@ -1,4 +1,3 @@ -import { ErrorBoundary } from '@sentry/react' import React from 'react' import { useLocation } from 'react-router' import { useClaimInfo } from 'state/claim/hooks' @@ -40,9 +39,7 @@ export default function Claim() { - - - + diff --git a/src/pages/CreateProposal/ProposalActionDetail.tsx b/src/pages/CreateProposal/ProposalActionDetail.tsx index 5471bbc5487..2745320d34a 100644 --- a/src/pages/CreateProposal/ProposalActionDetail.tsx +++ b/src/pages/CreateProposal/ProposalActionDetail.tsx @@ -1,6 +1,6 @@ -import { Token } from '@ubeswap/sdk' import AddressInputPanel from 'components/AddressInputPanel' import CurrencyInputPanel from 'components/CurrencyInputPanel' +import { Token } from 'lib/token-utils' import React from 'react' import styled from 'styled-components/macro' diff --git a/src/pages/CreateProposal/index.tsx b/src/pages/CreateProposal/index.tsx index 331a57a7b19..d5b34890712 100644 --- a/src/pages/CreateProposal/index.tsx +++ b/src/pages/CreateProposal/index.tsx @@ -1,12 +1,12 @@ import { defaultAbiCoder } from '@ethersproject/abi' import { getAddress, isAddress } from '@ethersproject/address' -import { Token, TokenAmount } from '@ubeswap/sdk' import { ButtonError } from 'components/Button' import { BlueCard } from 'components/Card' import { AutoColumn } from 'components/Column' -import { GAUGE_CONTROLLER, GAUGE_PROXY } from 'constants/StablePools' +import { GAUGE_CONTROLLER, GAUGE_PROXY } from 'constants/staking' import { useWeb3Context } from 'hooks' import JSBI from 'jsbi' +import { Token, TokenAmount } from 'lib/token-utils' import AppBody from 'pages/AppBody' import { Wrapper } from 'pages/Pool/styleds' import React, { useCallback, useMemo, useState } from 'react' @@ -49,7 +49,7 @@ const CreateProposalButton = ({ const formattedProposalThreshold = proposalThreshold ? JSBI.divide( proposalThreshold.quotient, - JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(proposalThreshold.currency.decimals)) + JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(proposalThreshold.token.decimals)) ).toLocaleString() : undefined return ( diff --git a/src/pages/Mento/index.tsx b/src/pages/Mento/index.tsx index 3debc3224aa..df1ef22da97 100644 --- a/src/pages/Mento/index.tsx +++ b/src/pages/Mento/index.tsx @@ -1,6 +1,7 @@ -import { JSBI, TokenAmount } from '@ubeswap/sdk' import { describeTrade } from 'components/swap/routing/describeTrade' import { useMentoTradeCallback } from 'components/swap/routing/useMentoTradeCallback' +import JSBI from 'jsbi' +import { TokenAmount } from 'lib/token-utils' import React, { useCallback, useContext, useEffect, useState } from 'react' import { isMobile } from 'react-device-detect' import { ArrowDown } from 'react-feather' @@ -137,7 +138,7 @@ export default function Mento() { ReactGA.event({ category: 'Swap', action: 'Swap', - label: [trade?.input?.currency?.symbol, trade?.output?.currency?.symbol].join('/'), + label: [trade?.input?.token?.symbol, trade?.output?.token?.symbol].join('/'), }) }) .catch((error) => { @@ -186,9 +187,13 @@ export default function Mento() { [onCurrencySelection] ) - const handleMaxInput = useCallback(() => { - maxAmountInput && onUserInput(Field.INPUT, maxAmountInput.toExact()) - }, [maxAmountInput, onUserInput]) + const handleMaxInput = useCallback( + (amount?: TokenAmount) => { + ;(amount && onUserInput(Field.INPUT, amount.toExact())) || + (maxAmountInput && onUserInput(Field.INPUT, maxAmountInput.toExact())) + }, + [maxAmountInput, onUserInput] + ) const handleOutputSelect = useCallback( (outputCurrency) => onCurrencySelection(Field.OUTPUT, outputCurrency), diff --git a/src/pages/Mento/redirects.tsx b/src/pages/Mento/redirects.tsx deleted file mode 100644 index 48680c38d42..00000000000 --- a/src/pages/Mento/redirects.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React, { useEffect } from 'react' -import { useDispatch } from 'react-redux' -import { Redirect, RouteComponentProps } from 'react-router-dom' - -import { AppDispatch } from '../../state' -import { ApplicationModal, setOpenModal } from '../../state/application/actions' - -// Redirects to swap but only replace the pathname -export function RedirectPathToSwapOnly({ location }: RouteComponentProps) { - return -} - -// Redirects from the /swap/:outputCurrency path to the /swap?outputCurrency=:outputCurrency format -export function RedirectToSwap(props: RouteComponentProps<{ outputCurrency: string }>) { - const { - location: { search }, - match: { - params: { outputCurrency }, - }, - } = props - - return ( - 1 - ? `${search}&outputCurrency=${outputCurrency}` - : `?outputCurrency=${outputCurrency}`, - }} - /> - ) -} - -export function OpenClaimAddressModalAndRedirectToSwap(props: RouteComponentProps) { - const dispatch = useDispatch() - useEffect(() => { - dispatch(setOpenModal(ApplicationModal.ADDRESS_CLAIM)) - }, [dispatch]) - return -} diff --git a/src/pages/OpenSum/index.tsx b/src/pages/OpenSum/index.tsx index 3bd63c7799f..6057062b8ee 100644 --- a/src/pages/OpenSum/index.tsx +++ b/src/pages/OpenSum/index.tsx @@ -1,6 +1,6 @@ -import { Token, TokenAmount } from '@ubeswap/sdk' import { useConstantSumContract } from 'hooks/useContract' import useTransactionDeadline from 'hooks/useTransactionDeadline' +import { Token, TokenAmount } from 'lib/token-utils' import React, { useCallback, useContext, useEffect, useState } from 'react' import { isMobile } from 'react-device-detect' import { ArrowDown } from 'react-feather' @@ -28,7 +28,6 @@ import { CHAIN } from '../../constants' import { useWeb3Context } from '../../hooks' import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback' import { MentoTrade } from '../../state/mento/hooks' -import { useIsDarkMode } from '../../state/user/hooks' import { TYPE } from '../../theme' import AppBody from '../AppBody' @@ -40,8 +39,6 @@ const VoteCard = styled(DataCard)` // #fbcc5c export default function OpenSum() { - const isDarkMode = useIsDarkMode() - const { address, connect, connected } = useWeb3Context() const theme = useContext(ThemeContext) @@ -111,7 +108,7 @@ export default function OpenSum() { ReactGA.event({ category: 'Swap', action: 'Swap', - label: [input?.currency?.symbol, output?.currency?.symbol].join('/'), + label: [input?.token?.symbol, output?.token?.symbol].join('/'), }) ReactGA.event({ @@ -136,8 +133,8 @@ export default function OpenSum() { inputValue, inputToken?.symbol, outputToken?.symbol, - input?.currency?.symbol, - output?.currency?.symbol, + input?.token?.symbol, + output?.token?.symbol, ]) // TODO: check for endless loop @@ -177,9 +174,12 @@ export default function OpenSum() { [setInputToken, setOutputToken, setApprovalSubmitted] ) - const handleMaxInput = useCallback(() => { - maxAmountInput && setInputValue(maxAmountInput.toExact()) - }, [maxAmountInput, setInputValue]) + const handleMaxInput = useCallback( + (amount?: TokenAmount) => { + ;(amount && setInputValue(amount.toExact())) || (maxAmountInput && setInputValue(maxAmountInput.toExact())) + }, + [maxAmountInput] + ) const onSwitchTokens = () => { alert('You can only swap FROM v1 assets TO v2 assets') @@ -342,7 +342,7 @@ export default function OpenSum() { disabled={!isValid} error={!isValid} > - + {swapInputError ? swapInputError : `${actionLabel}`}
diff --git a/src/pages/Pool/Manage.tsx b/src/pages/Pool/Manage.tsx index 8b4f0e8b80d..4c913c7ff68 100644 --- a/src/pages/Pool/Manage.tsx +++ b/src/pages/Pool/Manage.tsx @@ -1,29 +1,30 @@ -import { Fraction, JSBI, TokenAmount } from '@ubeswap/sdk' import CurrencyPoolLogo from 'components/CurrencyPoolLogo' import ExternalRewardsModal from 'components/earn/ClaimExternalRewardsModal' -import Loader from 'components/Loader' -import QuestionHelper from 'components/QuestionHelper' +import ClaimRewardModal from 'components/earn/ClaimRewardModal' +import StakingModal from 'components/earn/StakingModal' +import UnstakingModal from 'components/earn/UnstakingModal' import { useMobi } from 'hooks/Tokens' +import JSBI from 'jsbi' +import { calculateVirtualPrice } from 'lib/calculator' +import { Fraction, TokenAmount } from 'lib/token-utils' import React, { useCallback, useState } from 'react' import { Link, RouteComponentProps } from 'react-router-dom' -import { useExternalRewards, useStablePoolInfoByName } from 'state/stablePools/hooks' +import { usePegPrice } from 'state/application/hooks' +import { useExternalRewards, useGaugeInfo, useUserGaugeInfo } from 'state/gauges/hooks' +import { getCurrentDisplayFromGauge, useCurrentPoolAddress } from 'state/mobiusPools/hooks' +import { useStakingInfo } from 'state/staking/hooks' import styled from 'styled-components' import { CountUp } from 'use-count-up' -import { getDepositValues } from 'utils/stableSwaps' import { ButtonEmpty, ButtonPrimary } from '../../components/Button' import { AutoColumn } from '../../components/Column' -import ClaimRewardModal from '../../components/earn/ClaimRewardModal' -import StakingModal from '../../components/earn/StakingModal' import { CardNoise, CardSection, DataCard } from '../../components/earn/styled' -import UnstakingModal from '../../components/earn/UnstakingModal' import { AutoRow, RowBetween, RowFixed } from '../../components/Row' import { BIG_INT_SECONDS_IN_WEEK } from '../../constants' import { useWeb3Context } from '../../hooks' -import { useColor } from '../../hooks/useColor' import usePrevious from '../../hooks/usePrevious' import { useTokenBalance } from '../../state/wallet/hooks' -import { ExternalLinkIcon, TYPE } from '../../theme' +import { TYPE } from '../../theme' const PageWrapper = styled(AutoColumn)` margin-top: 3rem; @@ -44,15 +45,9 @@ const BottomSection = styled(AutoColumn)` position: relative; ` -const StyledDataCard = styled(DataCard)<{ bgColor?: any; showBackground?: any }>` - background: radial-gradient(76.02% 75.41% at 1.84% 0%, #1e1a31 0%, #3d51a5 100%); +const StyledDataCard = styled(DataCard)` + background: radial-gradient(50% 50% at 10% 10%, #35d07f 0%, #3488ec 180%); z-index: 2; - background: ${({ theme, bgColor, showBackground }) => - `radial-gradient(91.85% 100% at 1.84% 0%, ${bgColor} 0%, ${showBackground ? theme.black : theme.bg5} 100%) `}; - ${({ showBackground }) => - showBackground && - ` box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), - 0px 24px 32px rgba(0, 0, 0, 0.01);`} ` const StyledBottomCard = styled(DataCard)<{ dim: any }>` @@ -72,7 +67,7 @@ const PoolData = styled(DataCard)` ` const VoteCard = styled(DataCard)` - background: radial-gradient(76.02% 75.41% at 1.84% 0%, #27ae60 0%, #000000 100%); + background: radial-gradient(50% 50% at 10% 10%, #35d07f 0%, #3488ec 180%); overflow: hidden; ` @@ -99,94 +94,28 @@ const MS_IN_MINUTE = 1000 * 60 export default function Manage({ match: { - params: { poolName }, + params: { gaugeAddress }, }, -}: RouteComponentProps<{ poolName: string }>) { +}: RouteComponentProps<{ gaugeAddress: string }>) { const { connect, connected, address } = useWeb3Context() const mobi = useMobi() - const externalRewards = useExternalRewards({ address: poolName }) - console.log(externalRewards, poolName, 101) + const stakingInfo = useStakingInfo() + const display = getCurrentDisplayFromGauge(gaugeAddress) + const gaugeInfo = useGaugeInfo(display?.gauge ?? undefined) + const userGaugeInfo = useUserGaugeInfo(display?.gauge ?? undefined) + const exchangeInfo = useCurrentPoolAddress(display?.pool.address ?? '') - // get currencies and pair - - const stakingInfo = useStablePoolInfoByName(poolName) - - const { balances, stakedAmount, totalStakedAmount, tokens, peggedTo, pegComesAfter, lastClaim } = stakingInfo ?? { - balances: [], - stakedAmount: undefined, - totalStakedAmount: undefined, - tokens: [], - } - - const nextClaimableTime = lastClaim?.valueOf() + MS_IN_HOUR - const minutesUntilRefresh = Math.max(0, (nextClaimableTime - Date.now()) / MS_IN_MINUTE) - - const earnedMobi = new TokenAmount(mobi, stakingInfo?.pendingMobi ?? '0') - - const { valueOfStaked, totalValueDeposited } = getDepositValues(stakingInfo) - - let userMobiRate = new TokenAmount(mobi, JSBI.BigInt('0')) - if ( - stakingInfo && - totalStakedAmount && - totalStakedAmount.greaterThan('0') && - stakingInfo?.workingPercentage.greaterThan('0') - ) { - userMobiRate = new TokenAmount( - mobi, - stakingInfo?.workingPercentage.multiply(stakingInfo?.mobiRate ?? '0').toFixed(0) ?? '0' - ) - } - let userExternalRates: TokenAmount[] = [] - if ( - connected && - stakingInfo && - stakingInfo.externalRewardRates && - totalStakedAmount && - totalStakedAmount.greaterThan('0') && - stakingInfo?.workingPercentage.greaterThan('0') - ) { - userExternalRates = stakingInfo.externalRewardRates.map( - (rate) => new TokenAmount(rate.token, stakingInfo.totalPercentage.multiply(rate.raw).toFixed(0)) - ) - } - - const totalMobiRate = new TokenAmount(mobi, stakingInfo?.mobiRate ?? JSBI.BigInt('0')) - - const userBalances = balances.map((amount) => { - const fraction = new Fraction(stakedAmount?.raw.toString() ?? '0', totalStakedAmount?.raw || JSBI.BigInt('0')) - const ratio = fraction.multiply(amount.raw) - - if (JSBI.equal(ratio.denominator, JSBI.BigInt('0'))) { - return new TokenAmount(amount.currency, JSBI.BigInt('0')) - } - return new TokenAmount(amount.currency, JSBI.divide(ratio.numerator, ratio.denominator)) - }) - - const decimalPlacesForLP = stakedAmount?.greaterThan('1') ? 6 : stakedAmount?.greaterThan('0') ? 12 : 2 - - // detect existing unstaked LP position to show add button if none found - const userLiquidityUnstaked = useTokenBalance(connected ? address : undefined, stakingInfo?.lpToken) - const showAddLiquidityButton = Boolean(stakingInfo?.stakedAmount?.equalTo('0') && userLiquidityUnstaked?.equalTo('0')) - - // toggle for staking modal and unstaking modal + const userLiquidityUnstaked = useTokenBalance(connected ? address : undefined, display?.pool.lpToken) const [showStakingModal, setShowStakingModal] = useState(false) const [showUnstakingModal, setShowUnstakingModal] = useState(false) const [showClaimRewardModal, setShowClaimRewardModal] = useState(false) const [showExternalRewardModal, setShowExternalRewardModal] = useState(false) + const pegPrice = usePegPrice(display?.peg.priceQuery ?? null) - // fade cards if nothing staked or nothing earned yet - const disableTop = !stakingInfo?.stakedAmount || stakingInfo.stakedAmount.equalTo(JSBI.BigInt(0)) - - const token = stakingInfo?.tokens[0] - const backgroundColor = useColor(token ?? undefined) - - // get CUSD value of staked LP tokens - - const mobiCountUpAmount = earnedMobi?.toFixed(6) ?? '0' + const mobiCountUpAmount = userGaugeInfo?.claimableMobi.toFixed(6) ?? '0' const mobiCountUpAmountPrevious = usePrevious(mobiCountUpAmount) ?? '0' - const countUpAmount = earnedMobi?.toFixed(6) ?? '0' - const countUpAmountPrevious = usePrevious(countUpAmount) ?? '0' + + const externalRewards = useExternalRewards(display?.gauge) const handleDepositClick = useCallback(() => { if (connected) { @@ -196,70 +125,82 @@ export default function Manage({ } }, [connect, connected]) + if (!display || !gaugeInfo || !userGaugeInfo || !exchangeInfo || !userLiquidityUnstaked || !display.gauge) return null + + const mobiRate = new TokenAmount(mobi, gaugeInfo.weight.multiply(stakingInfo.mobiRate).quotient ?? 0) + + const nextClaimableTime = gaugeInfo.lastClaim.valueOf() + MS_IN_HOUR + const minutesUntilRefresh = Math.max(0, (nextClaimableTime - Date.now()) / MS_IN_MINUTE) + + const totalDeposited = exchangeInfo.lpTotalSupply + + const userDeposited = new TokenAmount(display.pool.lpToken, userGaugeInfo.balance) + const virtualPrice = calculateVirtualPrice(exchangeInfo) + const userDepositedValue = + pegPrice && virtualPrice ? virtualPrice.multiply(totalDeposited).multiply(pegPrice) : new Fraction(0) + + const userMobiRate = new TokenAmount(mobi, mobiRate.multiply(userDeposited).divide(totalDeposited).quotient) + const userExternalRates = display.gauge.additionalRewards.map( + (reward) => new TokenAmount(reward.token, reward.multiply(userDeposited).divide(totalDeposited).quotient) + ) + + const decimalPlacesForLP = userDeposited.greaterThan('1') ? 6 : userDeposited.greaterThan('0') ? 12 : 2 + + // fade cards if nothing staked or nothing earned yet + const disableTop = userGaugeInfo.claimableMobi.equalTo(0) && userDeposited.equalTo(0) + + const format = (str: string): string => { + const peg = display.peg + return (peg.position === 'before' ? peg.symbol : '').concat(str).concat(peg.position === 'after' ? peg.symbol : '') + } + return ( - {!stakingInfo && } - {stakingInfo && ( - <> - - {stakingInfo.name} Liquidity Mining - - - - - - - Total deposits - - {totalValueDeposited - ? `${stakingInfo.pegComesAfter ? '' : stakingInfo.peggedTo}${ - totalValueDeposited.lessThan('1') - ? totalValueDeposited.toFixed(stakingInfo.displayDecimals, { - groupSeparator: ',', - }) - : totalValueDeposited.toFixed(stakingInfo.displayDecimals, { - groupSeparator: ',', - }) - } ${stakingInfo.pegComesAfter ? stakingInfo.peggedTo : ''}` - : '-'} - - - - - - Pool Rate - - {stakingInfo - ? totalMobiRate?.multiply(BIG_INT_SECONDS_IN_WEEK)?.toFixed(0, { groupSeparator: ',' }) ?? '-' - : '0'} - {' MOBI / week'} + <> + + {display.name} Liquidity Mining + + + + + + + Total deposits + + {format(totalDeposited.toFixed(display.peg.decimals, { groupSeparator: ',' }))} + + + + + + Pool Rate + + {mobiRate.multiply(BIG_INT_SECONDS_IN_WEEK)?.toFixed(0, { groupSeparator: ',' }) ?? '-'} + {' MOBI / week'} + + {display.gauge.additionalRewards.map((tokenRate) => ( + + {`${tokenRate?.multiply(BIG_INT_SECONDS_IN_WEEK)?.toFixed(0, { groupSeparator: ',' })} ${ + tokenRate.token.symbol + } / week`} - {stakingInfo && - stakingInfo.externalRewardRates && - stakingInfo.externalRewardRates.map((rate) => ( - - {`${rate?.multiply(BIG_INT_SECONDS_IN_WEEK)?.toFixed(0, { groupSeparator: ',' })} ${ - rate.token.symbol - } / week`} - - ))} - - - - - )} + ))} + + + + - {stakingInfo && showAddLiquidityButton && ( + {userDeposited.equalTo('0') && userLiquidityUnstaked.equalTo('0') && ( - Step 1. Get MOBI-LP Liquidity tokens + Step 1. Get MOBI-LP tokens - {`Mobi-LP tokens are required. Once you've added liquidity to the ${stakingInfo.name} pool you can stake your liquidity tokens on this page.`} + {`Mobi-LP tokens are required. Once you've added liquidity to the ${display.name} pool you can stake your liquidity tokens on this page.`} @@ -271,149 +212,145 @@ export default function Manage({ )} - {stakingInfo && ( - <> - setShowStakingModal(false)} - stakingInfo={stakingInfo} - userLiquidityUnstaked={userLiquidityUnstaked} - /> - setShowUnstakingModal(false)} - stakingInfo={stakingInfo} - /> - setShowClaimRewardModal(false)} - stakingInfo={stakingInfo} - /> - setShowExternalRewardModal(false)} - stakingInfo={stakingInfo} - /> - - )} - - {stakingInfo && ( - - - - - - - - Your staked liquidity deposits - - - - {stakingInfo?.stakedAmount?.toSignificant(decimalPlacesForLP) ?? '-'} - - - MOBI-LP {stakingInfo.tokens.map(({ symbol }) => symbol).join('-')} - - - {stakingInfo?.stakedAmount && stakingInfo.stakedAmount.greaterThan('0') && ( - - - - Current value:{' '} - {valueOfStaked - ? `${pegComesAfter ? '' : peggedTo}${valueOfStaked.toFixed(4, { - separator: ',', - })} ${pegComesAfter ? peggedTo : ''}` - : '--'} - - - `${balance?.toFixed(Math.min(decimalPlacesForLP, balance.token.decimals), { - groupSeparator: ',', - })} ${balance.token.symbol}` - ) - .join(', ')} - /> - - - )} - - - - + <> + setShowStakingModal(false)} + userDeposited={userDeposited} + totalDeposited={totalDeposited} + userLiquidityUnstaked={userLiquidityUnstaked} + gaugeAddress={display.gauge.address} + mobiRate={mobiRate} + /> + setShowClaimRewardModal(false)} + gaugeAddress={display.gauge.address} + userGaugeInfo={userGaugeInfo} + /> + setShowUnstakingModal(false)} + userDeposited={userDeposited} + gaugeAddress={display.gauge.address} + /> + setShowExternalRewardModal(false)} + gaugeAddress={display.gauge.address} + externalRewards={externalRewards} + /> + + + + + + - + -
- Your unclaimed rewards -
- {earnedMobi && ( - - setShowClaimRewardModal(true)} - > - Claim MOBI - - - )} + Your staked liquidity deposits
- - - - - - ⚡ - - {stakingInfo - ? userMobiRate?.multiply(BIG_INT_SECONDS_IN_WEEK)?.toSignificant(4, { groupSeparator: ',' }) ?? - '-' - : '0'} - {' MOBI / week'} - + + {userDeposited.toSignificant(decimalPlacesForLP) ?? '-'} + + + MOBI-LP {display.pool.tokens.map(({ symbol }) => symbol).join('-')} + - {externalRewards && ( - <> - - - External rewards refresh once per hour - setShowExternalRewardModal(true)} + {userDeposited.greaterThan('0') && ( + + + + Current value: $ + {userDepositedValue.toFixed(4, { + groupSeparator: ',', + })} + + {/* + `${balance?.toFixed(Math.min(decimalPlacesForLP, balance.token.decimals), { + groupSeparator: ',', + })} ${balance.token.symbol}` + ) + .join(', ')} + /> */} + + + )} +
+
+
+ + + + +
+ Your unclaimed rewards +
+ {userGaugeInfo.claimableMobi && ( + + setShowClaimRewardModal(true)} + > + Claim MOBI + + + )} +
+ + + + + + + ⚡ + + {stakingInfo + ? userMobiRate?.multiply(BIG_INT_SECONDS_IN_WEEK)?.toSignificant(4, { groupSeparator: ',' }) ?? '-' + : '0'} + {' MOBI / week'} + + + {display.gauge.additionalRewards.length > 0 && ( + <> + + + External rewards refresh once per hour + setShowExternalRewardModal(true)} + > + Claim External + + + + Next Refresh in {minutesUntilRefresh.toFixed(0)} minutes + + {externalRewards && + externalRewards.map((rewardToken, i) => ( + - Claim External - - - - Next Refresh in {minutesUntilRefresh.toFixed(0)} minutes - - {externalRewards.map((reward, i) => ( - - + {rewardToken.toFixed(4, { groupSeparator: ',' })} @@ -424,60 +361,34 @@ export default function Manage({ ?.multiply(BIG_INT_SECONDS_IN_WEEK) ?.toSignificant(4, { groupSeparator: ',' }) ?? '-' : '0'} - {` ${stakingInfo.externalRewardRates?.[i].token.symbol} / week`} + {` ${rewardToken.token.symbol} / week`} ))} - - )} -
-
-
- {/* - - ⭐️ - - When you withdraw, the contract will automagically claim MOBI on your behalf! - */} - - {!showAddLiquidityButton && ( - - {stakingInfo && ( - - {stakingInfo?.stakedAmount?.greaterThan(JSBI.BigInt(0)) ? 'Deposit' : 'Deposit MOBI-LP Tokens'} - - )} - - {stakingInfo?.stakedAmount?.greaterThan(JSBI.BigInt(0)) && ( - <> - setShowUnstakingModal(true)} - > - Withdraw - )} - {/* {stakingInfo && !stakingInfo.active && ( - - Staking Rewards inactive for this pair. - - )} */} - + +
+
+ + {!userLiquidityUnstaked.equalTo('0') && ( + + Deposit + )} - {!userLiquidityUnstaked ? null : userLiquidityUnstaked.equalTo('0') ? null : !stakingInfo ? null : ( - {userLiquidityUnstaked.toSignificant(6)} MOBI LP tokens available + + {!userDeposited.equalTo('0') && ( + <> + setShowUnstakingModal(true)}> + Withdraw + + )} -
- )} + + {!userLiquidityUnstaked.equalTo('0') && ( + {userLiquidityUnstaked.toSignificant(6)} MOBI LP tokens available + )} +
) } - -const PairLinkIcon = styled(ExternalLinkIcon)` - svg { - stroke: ${(props) => props.theme.primary1}; - } -` diff --git a/src/pages/Pool/index.tsx b/src/pages/Pool/index.tsx index 50baa87b64c..dce0e61847c 100644 --- a/src/pages/Pool/index.tsx +++ b/src/pages/Pool/index.tsx @@ -1,22 +1,23 @@ -import { ErrorBoundary } from '@sentry/react' -import { cUSD, JSBI, TokenAmount } from '@ubeswap/sdk' import QuestionHelper from 'components/QuestionHelper' import { RowFixed } from 'components/Row' -import { Chain, Coins, PRICE } from 'constants/StablePools' -import { useMobi } from 'hooks/Tokens' -import React from 'react' +import { Chain, DisplayPool, IExchangeInfo, StablePools, Volume } from 'constants/pools' +import { useValueOfAllPools } from 'hooks/useStablePools' +import JSBI from 'jsbi' +import { TokenAmount } from 'lib/token-utils' +import React, { useCallback, useMemo } from 'react' import { isMobile } from 'react-device-detect' +import { useMobiPrice } from 'state/application/hooks' +import { GaugeInfo, useAllGaugesInfo, useAllUserGaugesInfo, UserGaugeInfo } from 'state/gauges/hooks' +import { useAllLpBalances, usePools, usePoolsVolume } from 'state/mobiusPools/hooks' +import { useStakingInfo } from 'state/staking/hooks' import styled from 'styled-components' -import { useCUSDPrice } from 'utils/useCUSDPrice' import { AutoColumn } from '../../components/Column' import { StablePoolCard } from '../../components/earn/StablePoolCard' import { CardNoise, CardSection, DataCard } from '../../components/earn/styled' -import Loader from '../../components/Loader' import { Row, RowBetween } from '../../components/Row' import { InfoWrapper } from '../../components/swap/styleds' import { CHAIN } from '../../constants' -import { StablePoolInfo, useStablePoolInfo } from '../../state/stablePools/hooks' import { Sel, TYPE } from '../../theme' const PageWrapper = styled(AutoColumn)` @@ -58,58 +59,86 @@ const HeaderLinks = styled(Row)` align-items: center; ` +enum SpecialChain { + Other = 'other', + All = 'all', +} + +type SelectChain = SpecialChain | Chain + const OtherChains = new Set([Chain.Avax, Chain.Polygon, Chain.Celo]) +export type Meta = { + display: DisplayPool + userGauge: UserGaugeInfo | null + gauge: GaugeInfo | null + lpBalance: TokenAmount + exchangeInfo: IExchangeInfo + volume: Volume +} + export default function Pool() { - const stablePools = useStablePoolInfo() - const [selection, setSelection] = React.useState(Chain.All) + const userGauges = useAllUserGaugesInfo() + const gauges = useAllGaugesInfo() + const stakingInfo = useStakingInfo() + const exchanges = usePools() + const lpBalances = useAllLpBalances() + const volumes = usePoolsVolume() + + const [selection, setSelection] = React.useState(SpecialChain.All) const [showDeprecated, setShowDeprecated] = React.useState(false) - const tvl = stablePools - .filter((pool) => pool && pool.virtualPrice) - .reduce((accum, poolInfo) => { - const price = - poolInfo.coin === Coins.Bitcoin - ? JSBI.BigInt(PRICE[Coins.Bitcoin]) - : poolInfo.coin === Coins.Ether - ? JSBI.BigInt(PRICE[Coins.Ether]) - : JSBI.BigInt(PRICE[Coins.USD]) - const lpPrice = JSBI.divide( - JSBI.multiply(price, poolInfo.virtualPrice), - JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('18')) - ) - const priceDeposited = JSBI.multiply(poolInfo?.totalDeposited?.raw ?? JSBI.BigInt('0'), lpPrice) - return JSBI.add(accum, priceDeposited) - }, JSBI.BigInt('0')) - const tvlAsTokenAmount = new TokenAmount(cUSD[CHAIN], tvl) - const mobiprice = useCUSDPrice(useMobi()) - const sortCallback = (pool1: StablePoolInfo, pool2: StablePoolInfo) => { - if (!pool1 || !pool2) return true - const isStaking1 = pool1.amountDeposited?.greaterThan(JSBI.BigInt('0')) || pool1.stakedAmount?.greaterThan('0') - const isStaking2 = pool2.amountDeposited?.greaterThan(JSBI.BigInt('0')) || pool2.stakedAmount?.greaterThan('0') - if (isStaking1 && !isStaking2) return false - return true - } - const sortedFilterdPools = stablePools - ?.sort(sortCallback) - .filter( - (pool) => - selection === Chain.All || - selection === pool.displayChain || - (selection === Chain.Other && OtherChains.has(pool.displayChain)) - ) + const tvl = useValueOfAllPools() + const mobiprice = useMobiPrice() + const meta: Meta[] = useMemo( + () => + StablePools[CHAIN].map((el, i) => { + return { + display: el, + userGauge: userGauges[i], + gauge: gauges[i], + lpBalance: lpBalances[i], + exchangeInfo: exchanges[i], + volume: volumes[i], + } + }), + [exchanges, gauges, lpBalances, userGauges, volumes] + ) + + const sortCallback = useCallback((pool1: Meta, pool2: Meta) => { + const isStaking1 = + pool1.lpBalance.greaterThan(0) || (pool1.userGauge && JSBI.greaterThan(pool1.userGauge.balance, JSBI.BigInt(0))) + const isStaking2 = + pool2.lpBalance.greaterThan(0) || (pool2.userGauge && JSBI.greaterThan(pool2.userGauge?.balance, JSBI.BigInt(0))) + if (isStaking1 && !isStaking2) return 1 + return -1 + }, []) + + const sortedFilterdPools = useMemo( + () => + meta + .sort(sortCallback) + .filter( + (pool) => + selection === SpecialChain.All || + selection === pool.display.chain || + (selection === SpecialChain.Other && OtherChains.has(pool.display.chain)) + ), + [meta, selection, sortCallback] + ) + console.log('pools page') return ( - TVL: ${tvlAsTokenAmount.toFixed(0, { groupSeparator: ',' })} + TVL: ${tvl.toFixed(0, { groupSeparator: ',' })} {mobiprice && Latest MOBI Price: ${mobiprice.toFixed(3)}} - setSelection(Chain.All)} selected={selection === Chain.All}> + setSelection(SpecialChain.All)} selected={selection === SpecialChain.All}> ALL setSelection(Chain.Ethereum)} selected={selection === Chain.Ethereum}> @@ -121,7 +150,7 @@ export default function Pool() { setSelection(Chain.Terra)} selected={selection === Chain.Terra}> TERRA - setSelection(Chain.Other)} selected={selection === Chain.Other}> + setSelection(SpecialChain.Other)} selected={selection === SpecialChain.Other}> OTHER @@ -146,17 +175,11 @@ export default function Pool() { - {stablePools && stablePools?.length === 0 ? ( - - ) : ( - sortedFilterdPools - .filter((pool) => !pool.isKilled && !pool.disabled) - .map((pool) => ( - - - - )) - )} + {sortedFilterdPools + .filter((pool) => !pool.gauge?.isKilled) + .map((pool) => ( + + ))} @@ -179,12 +202,8 @@ export default function Pool() { {showDeprecated && sortedFilterdPools - .filter((pool) => pool.isKilled || pool.disabled) - .map((pool) => ( - - - - ))} + .filter((pool) => pool.gauge?.isKilled) + .map((pool) => )} diff --git a/src/pages/Staking/CalcBoost.tsx b/src/pages/Staking/CalcBoost.tsx index d4c9a539cb3..37e46cab2c1 100644 --- a/src/pages/Staking/CalcBoost.tsx +++ b/src/pages/Staking/CalcBoost.tsx @@ -1,26 +1,27 @@ -import { Fraction, JSBI, TokenAmount } from '@ubeswap/sdk' import { AutoColumn } from 'components/Column' import QuestionHelper from 'components/QuestionHelper' import { RowBetween } from 'components/Row' -import { useMobi, useVeMobi } from 'hooks/Tokens' +import { DisplayPool, StablePools } from 'constants/pools' +import { useVeMobi } from 'hooks/Tokens' import { useColor } from 'hooks/useColor' +import useTheme from 'hooks/useTheme' +import JSBI from 'jsbi' +import { TokenAmount } from 'lib/token-utils' import { darken } from 'polished' import React, { useCallback, useState } from 'react' +import { GaugeInfo, UserGaugeInfo } from 'state/gauges/hooks' import { tryParseAmount } from 'state/mento/hooks' -import { MobiStakingInfo, useMobiStakingInfo } from 'state/staking/hooks' -import { useTokenBalance } from 'state/wallet/hooks' +import { StakingInfo, UserStakingInfo } from 'state/staking/hooks' import styled from 'styled-components' -import { theme, TYPE } from 'theme' -import { calcEstimatedBoost, calcVotesForMaxBoost } from 'utils/calcExpectedVeMobi' +import { TYPE } from 'theme' import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg' import DoubleCurrencyLogo from '../../components/DoubleLogo' import CurrencySearchModal from '../../components/PoolSearchModal/CurrencySearchModal' +import { CHAIN } from '../../constants' import { useWeb3Context } from '../../hooks' -import { StablePoolInfo, useStablePoolInfo } from '../../state/stablePools/hooks' import { useIsDarkMode } from '../../state/user/hooks' -import ClaimAllMobiModal from './ClaimAllMobiModal' -import { CurrencyRow } from './IncreaseLockAmount' +import { CurrencyRow } from './Lock/IncreaseLockAmount' const Container = styled.div` width: 49%; @@ -37,7 +38,7 @@ const Container = styled.div` `} ` -const Wrapper = styled(AutoColumn)<{ showBackground: boolean; bgColor: any; activated: boolean }>` +const Wrapper = styled(AutoColumn)` border-radius: 12px; width: 100%; overflow: hidden; @@ -46,16 +47,10 @@ const Wrapper = styled(AutoColumn)<{ showBackground: boolean; bgColor: any; acti padding: 0.5rem; gap: 0.5rem; cursor: pointer; - opacity: ${({ activated }) => (activated ? 1 : 0.9)}; + opacity: 0.9; overflow: hidden; position: relative; - background: ${({ bgColor, theme }) => - `radial-gradient(91.85% 100% at 1.84% 0%, ${bgColor} 0%, ${theme.black} 100%) `}; - color: ${({ theme, showBackground }) => (showBackground ? theme.white : theme.text1)} !important; - ${({ showBackground }) => - showBackground && - ` box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), - 0px 24px 32px rgba(0, 0, 0, 0.01);`} + color: theme.white !important; ${({ theme }) => theme.mediaWidth.upToSmall` `} &:hover { @@ -129,26 +124,31 @@ const StyledDropDown = styled(DropDown)<{ selected: boolean }>` ` type PositionsProps = { - stakingInfo: MobiStakingInfo - unclaimedMobi: TokenAmount + stakingInfo: StakingInfo + userStakingInfo: UserStakingInfo + gauges: (GaugeInfo | null)[] + userGauges: (UserGaugeInfo | null)[] } -export default function CalcBoost({ stakingInfo }: PositionsProps) { - const stablePools = useStablePoolInfo() - const { address, connected } = useWeb3Context() - const { positions = [] } = stakingInfo - const loading = positions.length === 0 - const greaterThanZero = positions.filter(({ baseBalance }) => baseBalance.greaterThan('0')) - const [openModal, setOpenModal] = useState(false) - const mobi = useMobi() + +export default function CalcBoost({ stakingInfo, userStakingInfo, gauges, userGauges }: PositionsProps) { + const { connected } = useWeb3Context() + + const displays = StablePools[CHAIN] + const openPositions = userGauges.filter((ug) => ug !== null && JSBI.greaterThan(ug.balance, JSBI.BigInt('0'))) + const vemobi = useVeMobi() + const theme = useTheme() + const color = useColor() + const isDarkMode = useIsDarkMode() + const [lpInput, setLPInput] = useState('') const [veInput, setVEInput] = useState('') - const [pool, setPool] = useState(stablePools[0] ?? undefined) - const lpBalance = pool ? pool.amountDeposited : new TokenAmount(mobi, JSBI.BigInt(0)) - const veBalance = useTokenBalance(connected ? address : undefined, vemobi) - const isDarkMode = useIsDarkMode() - const color = useColor() - const staking = useMobiStakingInfo() + const [pool, setPool] = useState(undefined) + const index = pool ? displays.indexOf(pool) : null + const gauge = index ? userGauges[index] : null + + const lpBalance = pool && gauge ? new TokenAmount(pool.pool.lpToken, gauge.balance) : null + const [modalOpen, setModalOpen] = useState(false) const handleDismissSearch = useCallback(() => { setModalOpen(false) @@ -156,31 +156,25 @@ export default function CalcBoost({ stakingInfo }: PositionsProps) { const onCurrencySelect = useCallback( (currency) => { - setPool(stablePools.filter((x) => x.lpToken?.address === currency.address)[0]) + setPool(displays.filter((x) => x.pool.lpToken?.address === currency.address)[0]) }, - [stablePools] + [displays] ) - const stake = - staking.positions && pool - ? staking.positions.filter((s) => s.address.toLowerCase() === pool.gaugeAddress?.toLowerCase())[0] - : undefined + const veParse = tryParseAmount(veInput, vemobi) + const lpParse = pool ? tryParseAmount(lpInput, pool.pool.lpToken) : undefined - if (!vemobi) return null + // TODO: fix this math + // const boost = + // !veParse || !lpParse + // ? new Fraction(JSBI.BigInt(0)) + // : calcEstimatedBoost(stake, veParse.raw, staking.totalVotingPower.raw, lpParse.raw) - const veParse = tryParseAmount(veInput, vemobi) - const lpParse = pool ? tryParseAmount(lpInput, pool.lpToken) : undefined - const boost = - !veParse || !lpParse - ? new Fraction(JSBI.BigInt(0)) - : calcEstimatedBoost(stake, veParse.raw, staking.totalVotingPower.raw, lpParse.raw) - - const votes = !lpParse - ? new TokenAmount(vemobi, JSBI.BigInt(0)) - : calcVotesForMaxBoost(stake, staking.totalVotingPower.raw, lpParse.raw, vemobi) + // const votes = !lpParse + // ? new TokenAmount(vemobi, JSBI.BigInt(0)) + // : calcVotesForMaxBoost(stake, staking.totalVotingPower.raw, lpParse.raw, vemobi) return ( - setOpenModal(false)} summaries={greaterThanZero} /> Calculate Boosts Calculate how much MOBI you need to stake} /> @@ -200,7 +194,12 @@ export default function CalcBoost({ stakingInfo }: PositionsProps) { > {pool ? ( - + ) : null} {pool ? ( @@ -216,26 +215,30 @@ export default function CalcBoost({ stakingInfo }: PositionsProps) { isOpen={modalOpen} onDismiss={handleDismissSearch} onCurrencySelect={onCurrencySelect} - selectedCurrency={pool ? pool.lpToken : undefined} + selectedCurrency={pool?.pool.lpToken} />
- {!pool ? ( - Select a Pool - ) : ( + {pool && (
{/* Enter amount */} - + {/* Enter amount */} - + Boost - {boost.toFixed(2)}x + 4.9x veMOBI to get max boost - {votes.toFixed(2)} + 67,780.98
diff --git a/src/pages/Staking/ClaimAllMobiModal.tsx b/src/pages/Staking/ClaimAllMobiModal.tsx index eac01de4fbe..ae5ba58df54 100644 --- a/src/pages/Staking/ClaimAllMobiModal.tsx +++ b/src/pages/Staking/ClaimAllMobiModal.tsx @@ -1,8 +1,7 @@ import { TransactionResponse } from '@ethersproject/providers' -import { JSBI, TokenAmount } from '@ubeswap/sdk' -import { useMobi } from 'hooks/Tokens' +import { IGauge } from 'constants/pools' import React, { useState } from 'react' -import { GaugeSummary } from 'state/staking/hooks' +import { UserGaugeInfo } from 'state/gauges/hooks' import styled from 'styled-components' import { ButtonError } from '../../components/Button' @@ -14,6 +13,7 @@ import { useWeb3Context } from '../../hooks' import { useMobiMinterContract } from '../../hooks/useContract' import { useTransactionAdder } from '../../state/transactions/hooks' import { CloseIcon, TYPE } from '../../theme' +import { useAllClaimableMobi } from './' const ContentWrapper = styled(AutoColumn)` width: 100%; @@ -23,15 +23,13 @@ const ContentWrapper = styled(AutoColumn)` interface StakingModalProps { isOpen: boolean onDismiss: () => void - summaries: GaugeSummary[] + userGauges: UserGaugeInfo[] + gauges: IGauge[] } -export const getAllUnclaimedMobi = (summaries: GaugeSummary[]): JSBI => - summaries.reduce((accum, { unclaimedMobi }) => JSBI.add(accum, unclaimedMobi.raw), JSBI.BigInt(0)) - -export default function ClaimAllMobiModal({ isOpen, onDismiss, summaries }: StakingModalProps) { +export default function ClaimAllMobiModal({ isOpen, onDismiss, userGauges, gauges }: StakingModalProps) { const { connected } = useWeb3Context() - const mobi = useMobi() + const claimableMobi = useAllClaimableMobi(userGauges) // monitor call to help UI loading state const addTransaction = useTransactionAdder() @@ -46,9 +44,8 @@ export default function ClaimAllMobiModal({ isOpen, onDismiss, summaries }: Stak const minter = useMobiMinterContract() - const pendingMobi = new TokenAmount(mobi, getAllUnclaimedMobi(summaries)) const gaugeAddresses = [...Array(8).keys()].map((i) => - i < summaries.length ? summaries[i].address : '0x0000000000000000000000000000000000000000' + i < gauges.length ? gauges[i].address : '0x0000000000000000000000000000000000000000' ) async function onClaimReward() { @@ -82,14 +79,12 @@ export default function ClaimAllMobiModal({ isOpen, onDismiss, summaries }: Stak Claim - {pendingMobi && ( - - - {pendingMobi.toSignificant(6)} MOBI - - {`Across ${summaries.length} farms`} - - )} + + + {claimableMobi.toSignificant(6)} MOBI + + {`Across ${gauges.length} farms`} + {error ?? 'Claim'} @@ -98,7 +93,7 @@ export default function ClaimAllMobiModal({ isOpen, onDismiss, summaries }: Stak {attempting && !hash && ( - Claiming {pendingMobi.toSignificant(6)} MOBI + Claiming {claimableMobi.toSignificant(6)} MOBI )} diff --git a/src/pages/Staking/GaugeVoteModal.tsx b/src/pages/Staking/GaugeVoteModal.tsx index a65fbcae978..4db90b98e2c 100644 --- a/src/pages/Staking/GaugeVoteModal.tsx +++ b/src/pages/Staking/GaugeVoteModal.tsx @@ -1,8 +1,9 @@ import { TransactionResponse } from '@ethersproject/providers' -import { JSBI } from '@ubeswap/sdk' +import { IGauge } from 'constants/pools' +import { useVotePowerLeft } from 'hooks/useStaking' import { MaxButton } from 'pages/Pool/styleds' import React, { useState } from 'react' -import { GaugeSummary, useVotePowerLeft } from 'state/staking/hooks' +import { UserGaugeInfo } from 'state/gauges/hooks' import styled from 'styled-components' import { ButtonError } from '../../components/Button' @@ -24,18 +25,16 @@ const ContentWrapper = styled(AutoColumn)` interface GaugeVoteModalProps { isOpen: boolean onDismiss: () => void - summary: GaugeSummary - disabled: boolean + userGauge: UserGaugeInfo + gauge: IGauge + poolName: string } -export const getAllUnclaimedMobi = (summaries: GaugeSummary[]): JSBI => - summaries.reduce((accum, { unclaimedMobi }) => JSBI.add(accum, unclaimedMobi.raw), JSBI.BigInt(0)) - function daysBetween(d1: Date, d2: Date): number { return Math.floor((d1.getTime() - d2.getTime()) / (1000 * 3600 * 24)) } -export default function GaugeVoteModal({ isOpen, onDismiss, summary, disabled }: GaugeVoteModalProps) { +export default function GaugeVoteModal({ isOpen, onDismiss, userGauge, poolName, gauge }: GaugeVoteModalProps) { const { connected } = useWeb3Context() // monitor call to help UI loading state @@ -43,9 +42,8 @@ export default function GaugeVoteModal({ isOpen, onDismiss, summary, disabled }: const [hash, setHash] = useState() const [attempting, setAttempting] = useState(false) const [input, setInput] = useState(0) - const votesLeft = useVotePowerLeft() + summary.powerAllocated + const votesLeft = useVotePowerLeft() + userGauge.powerAllocated const today = new Date(Date.now()) - const lastVote = summary.lastVote function wrappedOnDismiss() { setHash(undefined) @@ -59,10 +57,10 @@ export default function GaugeVoteModal({ isOpen, onDismiss, summary, disabled }: if (controller) { setAttempting(true) await controller - .vote_for_gauge_weights(summary.address, input * 100, { gasLimit: 350000 }) + .vote_for_gauge_weights(gauge.address, input * 100, { gasLimit: 350000 }) .then((response: TransactionResponse) => { addTransaction(response, { - summary: `Voted ${disabled ? 'to burn' : `for ${summary.pool} to receive`} ${input}% of MOBI inflation`, + summary: `Voted for ${poolName} to receive ${input}% of MOBI inflation`, }) setHash(response.hash) }) @@ -78,8 +76,8 @@ export default function GaugeVoteModal({ isOpen, onDismiss, summary, disabled }: error = 'Connect Wallet' } - if (daysBetween(today, lastVote) < 10) { - error = error ?? `Wait ${10 - daysBetween(today, lastVote)} days to vote for this pool again` + if (daysBetween(today, new Date(userGauge.lastVote)) < 10) { + error = error ?? `Wait ${10 - daysBetween(today, new Date(userGauge.lastVote))} days to vote for this pool again` } return ( @@ -87,7 +85,7 @@ export default function GaugeVoteModal({ isOpen, onDismiss, summary, disabled }: {!attempting && !hash && ( - Vote for {disabled ? 'BURN' : summary.pool} + Vote for {poolName} {votesLeft === 0 ? ( @@ -123,9 +121,7 @@ export default function GaugeVoteModal({ isOpen, onDismiss, summary, disabled }: - {`Vote ${ - disabled ? 'to burn' : `for ${summary.pool} to receive` - } ${input}% of MOBI inflation`} + {`Vote for ${poolName} to receive ${input}% of MOBI inflation`} {error ?? `Vote!`} @@ -145,7 +141,7 @@ export default function GaugeVoteModal({ isOpen, onDismiss, summary, disabled }: Transaction Submitted - veMOBI allocated to {summary.pool} + veMOBI allocated to {poolName} )} diff --git a/src/pages/Staking/GaugeWeights.tsx b/src/pages/Staking/GaugeWeights.tsx index c4279b3fb91..acdf20bb3a8 100644 --- a/src/pages/Staking/GaugeWeights.tsx +++ b/src/pages/Staking/GaugeWeights.tsx @@ -1,21 +1,20 @@ -import { CardNoise } from 'components/claim/styled' import { AutoColumn } from 'components/Column' import Loader from 'components/Loader' import { AutoRow, RowBetween, RowFixed } from 'components/Row' -import { useColor } from 'hooks/useColor' +import { StablePools } from 'constants/pools' import { useWindowSize } from 'hooks/useWindowSize' +import JSBI from 'jsbi' import { darken } from 'polished' -import React, { useState } from 'react' +import React from 'react' import { isMobile } from 'react-device-detect' import { RadialChart } from 'react-vis' -import { GaugeSummary } from 'state/staking/hooks' -import { useIsDarkMode } from 'state/user/hooks' +import { GaugeInfo } from 'state/gauges/hooks' import styled from 'styled-components' import { TYPE } from 'theme' -import GaugeVoteModal from './GaugeVoteModal' +import { CHAIN } from '../../constants' -const Wrapper = styled(AutoColumn)<{ showBackground: boolean; background: any }>` +const Wrapper = styled(AutoColumn)` border-radius: 12px; width: 100%; overflow: hidden; @@ -23,13 +22,9 @@ const Wrapper = styled(AutoColumn)<{ showBackground: boolean; background: any }> padding: 1rem; background: ${({ theme }) => theme.bg1}; color: ${({ theme }) => theme.text1} !important; - ${({ showBackground }) => - showBackground && - ` box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), - 0px 24px 32px rgba(0, 0, 0, 0.01);`} ${({ theme }) => theme.mediaWidth.upToSmall` `} -margin-top: 1rem; + margin-top: 1rem; ` const WrappedRow = styled.div` display: flex; @@ -38,13 +33,6 @@ const WrappedRow = styled.div` justify-content: center; ` -const CardContainer = styled.div` - display: flex; - flex-wrap: wrap; - margin-top: 1rem; - justify-content: space-between; -` - const ColorBox = styled.div<{ color: string }>` background: ${({ color }) => color}; width: 20px; @@ -57,21 +45,21 @@ const ColorBox = styled.div<{ color: string }>` const colorsForChart = ['#35D07F', '#73DDFF', '#BF97FF', '#3488EC', '#FB7C6D', '#FBCC5C', '#FEF2D6'] interface GaugeWeightsProps { - summaries: GaugeSummary[] + gauges: (GaugeInfo | null)[] } // TO DO: Account for Vote Power Allocations -export default function GaugeWeights({ summaries }: GaugeWeightsProps) { +export default function GaugeWeights({ gauges }: GaugeWeightsProps) { const numColors = colorsForChart.length - const data = summaries.map((summary, i) => ({ - label: summary.pool, - angle: parseInt(summary.currentWeight.multiply('360').toFixed(0)), - radius: summary.workingBalance.greaterThan('0') ? 10 : 9.5, - subLabel: `${summary.currentWeight.toFixed(2)}%`, + const data = gauges.map((g, i) => ({ + label: StablePools[CHAIN][i].name, + angle: parseInt(g?.weight.multiply('360').toFixed(0) ?? '0'), + radius: JSBI.greaterThan(g?.workingSupply ?? JSBI.BigInt(0), JSBI.BigInt(0)) ? 10 : 9.5, + subLabel: `${g?.weight.toFixed(2) ?? '0'}%`, color: darken(Math.floor(i / numColors) * 0.2, colorsForChart[i % numColors]), })) - const isDarkMode = useIsDarkMode() - const { width, height } = useWindowSize() + + const { width } = useWindowSize() return ( @@ -100,7 +88,7 @@ export default function GaugeWeights({ summaries }: GaugeWeightsProps) { /> {!isMobile && ( - {data.map(({ label, subLabel, color }) => ( + {data.map(({ label, color }) => ( {label} @@ -110,7 +98,7 @@ export default function GaugeWeights({ summaries }: GaugeWeightsProps) { {isMobile && ( - {data.map(({ label, subLabel, color }) => ( + {data.map(({ label, color }) => ( {label} @@ -122,80 +110,3 @@ export default function GaugeWeights({ summaries }: GaugeWeightsProps) { ) } - -const PositionWrapper = styled(AutoColumn)<{ - showBackground: boolean - bgColor: any - activated: boolean - disabled: boolean -}>` - border-radius: 12px; - width: 100%; - height: fit-content; - overflow: hidden; - position: relative; - margin-bottom: 1rem; - padding: 1rem; - cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')}; - opacity: ${({ activated }) => (activated ? 1 : 0.9)}; - overflow: hidden; - position: relative; - background: ${({ bgColor, theme }) => - `radial-gradient(91.85% 100% at 1.84% 0%, ${bgColor} 0%, ${theme.black} 100%) `}; - color: ${({ theme, showBackground }) => (showBackground ? theme.white : theme.text1)} !important; - ${({ showBackground }) => - showBackground && - ` box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), - 0px 24px 32px rgba(0, 0, 0, 0.01);`} - ${({ theme }) => theme.mediaWidth.upToSmall` -`} - &:hover { - opacity: 1; - } -` -const RowWithGap = styled(RowFixed)` - gap: 8px; -` - -function WeightCard({ - position, - showUserVote, - disabled, -}: { - position: GaugeSummary - showUserVote: boolean - disabled: boolean -}) { - const backgroundColor = useColor(position.firstToken) - const [voteModalOpen, setVoteModalOpen] = useState(false) - - return ( - <> - setVoteModalOpen(false)} /> - - !disabled && setVoteModalOpen(true)} - > - - - {position.pool} - {showUserVote ? ( - - Your Vote: - {`${position.powerAllocated.toFixed(2)}%`} - - ) : ( - - {`Current: ${position.currentWeight.toFixed(2)}%`} - {`Future: ${position.futureWeight.toFixed(2)}%`} - - )} - - - - ) -} diff --git a/src/pages/Staking/ExtendLock.tsx b/src/pages/Staking/Lock/ExtendLock.tsx similarity index 79% rename from src/pages/Staking/ExtendLock.tsx rename to src/pages/Staking/Lock/ExtendLock.tsx index 7c178202c87..872b78867ed 100644 --- a/src/pages/Staking/ExtendLock.tsx +++ b/src/pages/Staking/Lock/ExtendLock.tsx @@ -5,13 +5,12 @@ import { useDoTransaction } from 'hooks/useDoTransaction' import React, { useState } from 'react' import { Calendar } from 'react-date-range' import { Text } from 'rebass' -import { useLockEnd } from 'state/staking/hooks' -import { useIsDarkMode } from 'state/user/hooks' +import { useUserStakingState } from 'state/staking/hooks' -import { ButtonError } from '../../components/Button' -import { useWeb3Context } from '../../hooks' -import { useVotingEscrowContract } from '../../hooks/useContract' -import { TYPE } from '../../theme' +import { ButtonError } from '../../../components/Button' +import { useWeb3Context } from '../../../hooks' +import { useVotingEscrowContract } from '../../../hooks/useContract' +import { TYPE } from '../../../theme' const MILLISECONDS_PER_SECOND = 1000 const SECONDS_PER_WEEK = 604800 @@ -25,9 +24,8 @@ export default function ExtendLock({ setHash, setAttempting }: WithdrawModalProp const { connected } = useWeb3Context() // monitor call to help UI loading state - const endOfLock = useLockEnd() - const [date, setDate] = useState(new Date(endOfLock)) - const isDarkMode = useIsDarkMode() + const { lock } = useUserStakingState() + const [date, setDate] = useState(new Date(lock?.end ?? 0)) const roundedDate = date ? new Date( Math.floor(date?.valueOf() / (MILLISECONDS_PER_SECOND * SECONDS_PER_WEEK)) * @@ -59,7 +57,7 @@ export default function ExtendLock({ setHash, setAttempting }: WithdrawModalProp error = 'Connect Wallet' } - if (!roundedDate || roundedDate.getTime() < endOfLock) { + if (!roundedDate || roundedDate.getTime() < (lock?.end ?? 0)) { error = error ?? 'Choose a date later than your current lock' } @@ -73,7 +71,7 @@ export default function ExtendLock({ setHash, setAttempting }: WithdrawModalProp - + {error ? error : `Lock until ${roundedDate?.toLocaleDateString()}`} diff --git a/src/pages/Staking/IncreaseLockAmount.tsx b/src/pages/Staking/Lock/IncreaseLockAmount.tsx similarity index 67% rename from src/pages/Staking/IncreaseLockAmount.tsx rename to src/pages/Staking/Lock/IncreaseLockAmount.tsx index 6c55ef4a01a..dbca61cf772 100644 --- a/src/pages/Staking/IncreaseLockAmount.tsx +++ b/src/pages/Staking/Lock/IncreaseLockAmount.tsx @@ -1,32 +1,31 @@ import 'react-date-range/dist/styles.css' // main style file import 'react-date-range/dist/theme/default.css' // theme css file -import { JSBI, Token, TokenAmount } from '@ubeswap/sdk' import CurrencyLogo from 'components/CurrencyLogo' import Loader from 'components/Loader' import { AutoRow, RowBetween } from 'components/Row' +import { DisplayPool } from 'constants/pools' import { useMobi } from 'hooks/Tokens' import { useDoTransaction } from 'hooks/useDoTransaction' +import JSBI from 'jsbi' +import { Token, TokenAmount } from 'lib/token-utils' import React, { useState } from 'react' import { Text } from 'rebass' -import { StablePoolInfo } from 'state/stablePools/hooks' -import { useIsDarkMode } from 'state/user/hooks' import { useTokenBalance } from 'state/wallet/hooks' import styled from 'styled-components' +import invariant from 'tiny-invariant' + +import { ButtonConfirmed, ButtonError } from '../../../components/Button' +import Column from '../../../components/Column' +import DoubleCurrencyLogo from '../../../components/DoubleLogo' +import { Input as NumericalInput } from '../../../components/NumericalInput' +import ProgressSteps from '../../../components/ProgressSteps' +import { useWeb3Context } from '../../../hooks' +import { ApprovalState, useApproveCallback } from '../../../hooks/useApproveCallback' +import { useVotingEscrowContract } from '../../../hooks/useContract' +import { tryParseAmount } from '../../../state/swap/hooks' +import { TYPE } from '../../../theme' -import { ButtonConfirmed, ButtonError } from '../../components/Button' -import Column from '../../components/Column' -import DoubleCurrencyLogo from '../../components/DoubleLogo' -import { Input as NumericalInput } from '../../components/NumericalInput' -import ProgressSteps from '../../components/ProgressSteps' -import { useWeb3Context } from '../../hooks' -import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback' -import { useVotingEscrowContract } from '../../hooks/useContract' -import { tryParseAmount } from '../../state/swap/hooks' -import { TYPE } from '../../theme' - -const MILLISECONDS_PER_SECOND = 1000 -const SECONDS_PER_WEEK = 604800 interface IncreaseLockAmountProps { setAttempting: (attempting: boolean) => void setHash: (hash: string | undefined) => void @@ -41,10 +40,10 @@ export default function IncreaseLockAmount({ setHash, setAttempting }: IncreaseL const [approving, setApproving] = useState(false) const [input, setInput] = useState('') const selectedAmount = tryParseAmount(input, mobi) || new TokenAmount(mobi, '0') - const isDarkMode = useIsDarkMode() const doTransaction = useDoTransaction() const veMobiContract = useVotingEscrowContract() + invariant(veMobiContract, 'veMobi contract') const [approval, approveCallback] = useApproveCallback(selectedAmount, veMobiContract.address) const showApproveFlow = @@ -123,7 +122,7 @@ export default function IncreaseLockAmount({ setHash, setAttempting }: IncreaseL ) : ( - + {error ? error : `Increase Lock`} @@ -139,17 +138,12 @@ export default function IncreaseLockAmount({ setHash, setAttempting }: IncreaseL type CurrencyRowProps = { val: string + balance: TokenAmount | undefined token: Token setTokenAmount: (tokenAmount: string) => void - readOnly: boolean | undefined - balance?: TokenAmount - pool?: StablePoolInfo + pool?: DisplayPool } -const InputRowLeft = styled.div`` - -const TokenInfo = styled.div`` - const InputRow = styled.div<{ selected: boolean }>` ${({ theme }) => theme.flexRowNoWrap}; align-items: center; @@ -168,22 +162,6 @@ const Aligner = styled.span` justify-content: space-between; ` -const InputPanel = styled.div<{ hideInput?: boolean }>` - ${({ theme }) => theme.flexColumnNoWrap} - position: relative; - border-radius: ${({ hideInput }) => (hideInput ? '8px' : '20px')}; - background-color: ${({ theme }) => theme.bg2}; - z-index: 1; - width: 100%; -` - -const Container = styled.div<{ hideInput: boolean }>` - border-radius: ${({ hideInput }) => (hideInput ? '8px' : '20px')}; - border: 1px solid ${({ theme }) => theme.bg2}; - background-color: ${({ theme }) => theme.bg1}; - padding: 0.5rem; -` - const StyledTokenName = styled.span<{ active?: boolean }>` ${({ active }) => (active ? ' margin: 0 0.25rem 0 0.75rem;' : ' margin: 0 0.25rem 0 0.25rem;')} font-size: ${({ active }) => (active ? '20px' : '16px')}; @@ -193,33 +171,29 @@ const BalanceText = styled(TYPE.subHeader)` cursor: pointer; ` -export const CurrencyRow = ({ val, token, setTokenAmount, balance, readOnly, pool }: CurrencyRowProps) => { - const currency = token - const tokenBalance = balance - const TEN = JSBI.BigInt('10') - +export const CurrencyRow = ({ val, token, setTokenAmount, balance, pool }: CurrencyRowProps) => { const mainRow = ( - +
{pool ? ( - + ) : ( - + )} - - {(currency && currency.symbol && currency.symbol.length > 20 - ? currency.symbol.slice(0, 4) + - '...' + - currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length) - : currency?.symbol) || ''} + + {token.symbol ?? ''}
{ setTokenAmount(val) @@ -228,12 +202,12 @@ export const CurrencyRow = ({ val, token, setTokenAmount, balance, readOnly, poo
) - const decimalPlacesForBalance = tokenBalance?.greaterThan('1') ? 2 : tokenBalance?.greaterThan('0') ? 10 : 2 + const decimalPlacesForBalance = balance?.greaterThan('1') ? 2 : balance?.greaterThan('0') ? 10 : 2 - const balanceRow = !readOnly && ( + const balanceRow = (
- setTokenAmount(tokenBalance?.toExact() || '0')}> - Balance: {tokenBalance?.toFixed(decimalPlacesForBalance)} + setTokenAmount(balance?.toExact() || '0')}> + Balance: {balance?.toFixed(decimalPlacesForBalance)}
) @@ -245,11 +219,3 @@ export const CurrencyRow = ({ val, token, setTokenAmount, balance, readOnly, poo
) } - -const insertDecimal = (tokenAmount: TokenAmount) => { - const { token } = tokenAmount - const amount = tokenAmount.divide( - new TokenAmount(token, JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt(token.decimals))) - ) - return amount.toFixed(2) -} diff --git a/src/pages/Staking/Lock.tsx b/src/pages/Staking/Lock/Lock.tsx similarity index 74% rename from src/pages/Staking/Lock.tsx rename to src/pages/Staking/Lock/Lock.tsx index de780b06146..e8a0292f1e5 100644 --- a/src/pages/Staking/Lock.tsx +++ b/src/pages/Staking/Lock/Lock.tsx @@ -1,66 +1,74 @@ import 'react-date-range/dist/styles.css' // main style file import 'react-date-range/dist/theme/default.css' // theme css file -import { JSBI, Token, TokenAmount } from '@ubeswap/sdk' import CurrencyLogo from 'components/CurrencyLogo' import Loader from 'components/Loader' import { AutoRow, RowBetween } from 'components/Row' +import { StablePools } from 'constants/pools' import { useMobi } from 'hooks/Tokens' import { useDoTransaction } from 'hooks/useDoTransaction' +import JSBI from 'jsbi' +import { Token, TokenAmount } from 'lib/token-utils' import React, { useState } from 'react' import { Calendar } from 'react-date-range' import { Text } from 'rebass' -import { useMobiStakingInfo } from 'state/staking/hooks' -import { useIsDarkMode } from 'state/user/hooks' +import { GaugeInfo, UserGaugeInfo } from 'state/gauges/hooks' +import { StakingInfo } from 'state/staking/hooks' import { useTokenBalance } from 'state/wallet/hooks' import styled from 'styled-components' +import invariant from 'tiny-invariant' import { calcBoost, calcExpectedVeMobi } from 'utils/calcExpectedVeMobi' -import { ButtonConfirmed, ButtonError } from '../../components/Button' -import Column from '../../components/Column' -import { Input as NumericalInput } from '../../components/NumericalInput' -import ProgressSteps from '../../components/ProgressSteps' -import { useWeb3Context } from '../../hooks' -import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback' -import { useVotingEscrowContract } from '../../hooks/useContract' -import { tryParseAmount } from '../../state/swap/hooks' -import { TYPE } from '../../theme' +import { ButtonConfirmed, ButtonError } from '../../../components/Button' +import Column from '../../../components/Column' +import { Input as NumericalInput } from '../../../components/NumericalInput' +import ProgressSteps from '../../../components/ProgressSteps' +import { CHAIN } from '../../../constants' +import { useWeb3Context } from '../../../hooks' +import { ApprovalState, useApproveCallback } from '../../../hooks/useApproveCallback' +import { useVotingEscrowContract } from '../../../hooks/useContract' +import { tryParseAmount } from '../../../state/swap/hooks' +import { TYPE } from '../../../theme' const MILLISECONDS_PER_SECOND = 1000 const SECONDS_PER_WEEK = 604800 -export const roundDate = (date: Date) => +export const roundDate = (date: number) => new Date( - Math.floor(date.valueOf() / (MILLISECONDS_PER_SECOND * SECONDS_PER_WEEK)) * - (MILLISECONDS_PER_SECOND * SECONDS_PER_WEEK) + Math.floor(date / (MILLISECONDS_PER_SECOND * SECONDS_PER_WEEK)) * (MILLISECONDS_PER_SECOND * SECONDS_PER_WEEK) ) interface LockProps { setAttempting: (attempting: boolean) => void setHash: (hash: string | undefined) => void + userGauges: (UserGaugeInfo | null)[] + gauges: (GaugeInfo | null)[] + stakingInfo: StakingInfo } -export default function Lock({ setHash, setAttempting }: LockProps) { +export default function Lock({ setHash, setAttempting, userGauges, gauges, stakingInfo }: LockProps) { const { connected, address } = useWeb3Context() // monitor call to help UI loading state - const stakingInfo = useMobiStakingInfo() - const positions = stakingInfo.positions?.filter((pos) => pos.baseBalance.greaterThan('0')) ?? [] const mobi = useMobi() const balance = useTokenBalance(connected ? address : undefined, mobi) + const [approving, setApproving] = useState(false) const [input, setInput] = useState('') const [showBoosts, setShowBoosts] = useState(false) + const selectedAmount = tryParseAmount(input, mobi) || new TokenAmount(mobi, '0') const [date, setDate] = useState() - const isDarkMode = useIsDarkMode() - const roundedDate = date ? roundDate(date) : undefined + const roundedDate = date ? roundDate(date.getSeconds()) : undefined const doTransaction = useDoTransaction() const veMobiContract = useVotingEscrowContract() const expectedVeMobi = calcExpectedVeMobi( selectedAmount, - roundedDate ? roundedDate.getTime() - roundDate(new Date(Date.now())).getTime() : 0 + roundedDate ? roundedDate.getTime() - roundDate(Date.now()).getTime() : 0 ) + + invariant(veMobiContract, 'veMobi contract') + const [approval, approveCallback] = useApproveCallback(selectedAmount, veMobiContract.address) const showApproveFlow = approval === ApprovalState.NOT_APPROVED || @@ -117,15 +125,18 @@ export default function Lock({ setHash, setAttempting }: LockProps) { Show Boosts ) : ( - positions.map((pos) => { + userGauges.map((ug, i) => { + if (ug === null || JSBI.equal(ug.balance, JSBI.BigInt(0))) return null const boost = calcBoost( - pos, + ug?.balance, + gauges[i]?.totalSupply ?? JSBI.BigInt(0), expectedVeMobi.raw, JSBI.add(expectedVeMobi.raw, stakingInfo.totalVotingPower.raw) ) + const poolName = StablePools[CHAIN][i].name return ( - - {pos.pool} + + {poolName} {boost.toFixed(2)}x ) @@ -167,7 +178,7 @@ export default function Lock({ setHash, setAttempting }: LockProps) { ) : ( - + {error ? error : `Lock until ${roundedDate?.toLocaleDateString()}`} @@ -185,14 +196,9 @@ type CurrencyRowProps = { val: string token: Token setTokenAmount: (tokenAmount: string) => void - readOnly: boolean | undefined balance?: TokenAmount } -const InputRowLeft = styled.div`` - -const TokenInfo = styled.div`` - const InputRow = styled.div<{ selected: boolean }>` ${({ theme }) => theme.flexRowNoWrap}; align-items: center; @@ -211,22 +217,6 @@ const Aligner = styled.span` justify-content: space-between; ` -const InputPanel = styled.div<{ hideInput?: boolean }>` - ${({ theme }) => theme.flexColumnNoWrap} - position: relative; - border-radius: ${({ hideInput }) => (hideInput ? '8px' : '20px')}; - background-color: ${({ theme }) => theme.bg2}; - z-index: 1; - width: 100%; -` - -const Container = styled.div<{ hideInput: boolean }>` - border-radius: ${({ hideInput }) => (hideInput ? '8px' : '20px')}; - border: 1px solid ${({ theme }) => theme.bg2}; - background-color: ${({ theme }) => theme.bg1}; - padding: 0.5rem; -` - const StyledTokenName = styled.span<{ active?: boolean }>` ${({ active }) => (active ? ' margin: 0 0.25rem 0 0.75rem;' : ' margin: 0 0.25rem 0 0.25rem;')} font-size: ${({ active }) => (active ? '20px' : '16px')}; @@ -236,13 +226,12 @@ const BalanceText = styled(TYPE.subHeader)` cursor: pointer; ` -const CurrencyRow = ({ val, token, setTokenAmount, balance, readOnly }: CurrencyRowProps) => { +const CurrencyRow = ({ val, token, setTokenAmount, balance }: CurrencyRowProps) => { const currency = token const tokenBalance = balance - const TEN = JSBI.BigInt('10') const mainRow = ( - +
@@ -258,7 +247,6 @@ const CurrencyRow = ({ val, token, setTokenAmount, balance, readOnly }: Currency { setTokenAmount(val) @@ -269,7 +257,7 @@ const CurrencyRow = ({ val, token, setTokenAmount, balance, readOnly }: Currency ) const decimalPlacesForBalance = tokenBalance?.greaterThan('1') ? 2 : tokenBalance?.greaterThan('0') ? 10 : 2 - const balanceRow = !readOnly && ( + const balanceRow = (
setTokenAmount(tokenBalance?.toExact() || '0')}> Balance: {tokenBalance?.toFixed(decimalPlacesForBalance)} @@ -284,11 +272,3 @@ const CurrencyRow = ({ val, token, setTokenAmount, balance, readOnly }: Currency
) } - -const insertDecimal = (tokenAmount: TokenAmount) => { - const { token } = tokenAmount - const amount = tokenAmount.divide( - new TokenAmount(token, JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt(token.decimals))) - ) - return amount.toFixed(2) -} diff --git a/src/pages/Staking/LockModal.tsx b/src/pages/Staking/Lock/LockModal.tsx similarity index 76% rename from src/pages/Staking/LockModal.tsx rename to src/pages/Staking/Lock/LockModal.tsx index d6234f1976b..3dab73fc752 100644 --- a/src/pages/Staking/LockModal.tsx +++ b/src/pages/Staking/Lock/LockModal.tsx @@ -1,17 +1,20 @@ import { TransactionResponse } from '@ethersproject/abstract-provider' import { ButtonConfirmed } from 'components/Button' import Loader from 'components/Loader' +import { ExternalStakingRewards } from 'constants/staking' import { useStakingContract } from 'hooks/useContract' import React, { useState } from 'react' -import { useSNXRewardInfo } from 'state/staking/hooks' +import { GaugeInfo, UserGaugeInfo } from 'state/gauges/hooks' +import { StakingInfo } from 'state/staking/hooks' import { useTransactionAdder } from 'state/transactions/hooks' import styled from 'styled-components' -import { AutoColumn } from '../../components/Column' -import Modal from '../../components/Modal' -import { LoadingView, SubmittedView } from '../../components/ModalViews' -import { RowBetween } from '../../components/Row' -import { CloseIcon, TYPE } from '../../theme' +import { AutoColumn } from '../../../components/Column' +import Modal from '../../../components/Modal' +import { LoadingView, SubmittedView } from '../../../components/ModalViews' +import { RowBetween } from '../../../components/Row' +import { CHAIN } from '../../../constants' +import { CloseIcon, TYPE } from '../../../theme' import ExtendLock from './ExtendLock' import IncreaseLockAmount from './IncreaseLockAmount' import Lock from './Lock' @@ -30,6 +33,9 @@ interface LockModalProps { isOpen: boolean onDismiss: () => void lockType?: LockType + stakingInfo: StakingInfo + gauges: (GaugeInfo | null)[] + userGauges: (UserGaugeInfo | null)[] } const ModifiedWrapper = styled(ContentWrapper)` @@ -40,10 +46,16 @@ const ModifiedWrapper = styled(ContentWrapper)` } ` -export default function LockModal({ isOpen, onDismiss, lockType = LockType.initial }: LockModalProps) { +export default function LockModal({ + isOpen, + onDismiss, + lockType = LockType.initial, + stakingInfo, + gauges, + userGauges, +}: LockModalProps) { // monitor call to help UI loading state - const { snxAddress } = useSNXRewardInfo() - const stakingContract = useStakingContract(snxAddress) + const stakingContract = useStakingContract(ExternalStakingRewards[CHAIN]) const addTransaction = useTransactionAdder() const [hash, setHash] = useState() const [hasSnapshot, setHasSnapshot] = useState(0) @@ -92,7 +104,13 @@ export default function LockModal({ isOpen, onDismiss, lockType = LockType.initi {hasSnapshot === 0 ? Snapshot veMOBI Celo Rewards : } ) : lockType === LockType.initial ? ( - + ) : lockType === LockType.extend ? ( ) : ( diff --git a/src/pages/Staking/Stake.tsx b/src/pages/Staking/Lock/Stake.tsx similarity index 56% rename from src/pages/Staking/Stake.tsx rename to src/pages/Staking/Lock/Stake.tsx index 3cd7aa011fb..34ca69fac08 100644 --- a/src/pages/Staking/Stake.tsx +++ b/src/pages/Staking/Lock/Stake.tsx @@ -4,12 +4,13 @@ import { AutoColumn } from 'components/Column' import { RowBetween } from 'components/Row' import React, { useState } from 'react' import Countdown from 'react-countdown' -import { MobiStakingInfo } from 'state/staking/hooks' +import { GaugeInfo, UserGaugeInfo } from 'state/gauges/hooks' +import { StakingInfo, UserStakingInfo } from 'state/staking/hooks' import styled from 'styled-components' import { theme, TYPE } from 'theme' -import { useVotingEscrowContract } from '../../hooks/useContract' -import { useTransactionAdder } from '../../state/transactions/hooks' +import { useVotingEscrowContract } from '../../../hooks/useContract' +import { useTransactionAdder } from '../../../state/transactions/hooks' import LockModal, { LockType } from './LockModal' const SECONDS_IN_DAY = 24 * 60 * 60 @@ -29,7 +30,7 @@ const Container = styled.div` margin-bottom: 1rem; `} ` -const Wrapper = styled(AutoColumn)<{ showBackground: boolean; background: any }>` +const Wrapper = styled(AutoColumn)<{ showBackground: boolean }>` border-radius: 12px; width: 100%; overflow: hidden; @@ -48,14 +49,19 @@ const Wrapper = styled(AutoColumn)<{ showBackground: boolean; background: any }> ` type PropTypes = { - stakingInfo: MobiStakingInfo + userStakingInfo: UserStakingInfo + stakingInfo: StakingInfo + userGauges: (UserGaugeInfo | null)[] + gauges: (GaugeInfo | null)[] } -export default function Stake({ stakingInfo }: PropTypes) { - const { mobiLocked, lockEnd } = stakingInfo +export default function Stake({ userStakingInfo, stakingInfo, userGauges, gauges }: PropTypes) { + const { lock } = userStakingInfo const [lockType, setLockType] = useState(-1) + const veMobiContract = useVotingEscrowContract() const [attempting, setAttempting] = useState(false) const addTransaction = useTransactionAdder() + async function onClaim() { if (veMobiContract) { setAttempting(true) @@ -78,65 +84,68 @@ export default function Stake({ stakingInfo }: PropTypes) { return ( - -1} onDismiss={() => setLockType(-1)} lockType={lockType} /> - + -1} + onDismiss={() => setLockType(-1)} + lockType={lockType} + stakingInfo={stakingInfo} + userGauges={userGauges} + gauges={gauges} + /> + Locked MOBI: - {mobiLocked ? mobiLocked.toFixed(2) : '0.00'} + {lock.locked.toSignificant(2)} - {mobiLocked && mobiLocked.greaterThan('0') && Date.now() + SECONDS_IN_DAY >= (lockEnd?.valueOf() ?? 0) ? ( + {lock.locked.greaterThan('0') && Date.now() + SECONDS_IN_DAY >= lock.end ? ( You can claim in: - + - ) : mobiLocked && mobiLocked.greaterThan('0') ? ( + ) : lock.locked.greaterThan('0') ? ( You can claim on: - {lockEnd?.toLocaleDateString() ?? '--'} + {new Date(lock.end).toLocaleDateString()} ) : null} - {true && ( -
- {Date.now() > (lockEnd?.valueOf() ?? 0) && mobiLocked && mobiLocked.greaterThan('0') ? ( +
+ {Date.now() > lock.end && lock.locked.greaterThan('0') ? ( + + {attempting ? 'CLAIMING...' : 'CLAIM'} + + ) : ( + <> + lock.locked.greaterThan('0') ? setLockType(LockType.increase) : setLockType(LockType.initial) + } + style={{ fontWeight: 700, fontSize: 18, backgroundColor: theme(false).celoGreen }} > - {attempting ? 'CLAIMING...' : 'CLAIM'} + DEPOSIT - ) : ( - <> + {lock.locked.greaterThan('0') && ( - mobiLocked && mobiLocked.greaterThan('0') - ? setLockType(LockType.increase) - : setLockType(LockType.initial) - } - style={{ fontWeight: 700, fontSize: 18, backgroundColor: theme(false).celoGreen }} + onClick={() => setLockType(LockType.extend)} + style={{ fontWeight: 700, fontSize: 18, backgroundColor: theme(false).celoGold }} > - DEPOSIT + EXTEND - {mobiLocked && mobiLocked.greaterThan('0') && ( - setLockType(LockType.extend)} - style={{ fontWeight: 700, fontSize: 18, backgroundColor: theme(false).celoGold }} - > - EXTEND - - )} - - )} -
- )} + )} + + )} +
) diff --git a/src/pages/Staking/Positions.tsx b/src/pages/Staking/Positions.tsx index 0b34597f0bb..0ada108cd37 100644 --- a/src/pages/Staking/Positions.tsx +++ b/src/pages/Staking/Positions.tsx @@ -1,24 +1,26 @@ -import { JSBI, TokenAmount } from '@ubeswap/sdk' -import { ButtonOutlined, ButtonPrimary } from 'components/Button' +import { ButtonPrimary } from 'components/Button' import { AutoColumn } from 'components/Column' import { CardNoise } from 'components/earn/styled' -import Loader from 'components/Loader' -import { AutoRow, RowBetween, RowFixed } from 'components/Row' -import { ChainLogo } from 'constants/StablePools' +import { RowBetween, RowFixed } from 'components/Row' +import { ChainLogo, DisplayPool, IGauge, StablePools } from 'constants/pools' +import { CUSD } from 'constants/tokens' import { usePoolColor } from 'hooks/useColor' +import JSBI from 'jsbi' +import { TokenAmount } from 'lib/token-utils' import React, { useState } from 'react' import { isMobile } from 'react-device-detect' import { ChevronDown, ChevronUp } from 'react-feather' -import { usePriceOfLp } from 'state/stablePools/hooks' -import { GaugeSummary, MobiStakingInfo } from 'state/staking/hooks' +import { GaugeInfo, UserGaugeInfo } from 'state/gauges/hooks' +import { StakingInfo, UserStakingInfo } from 'state/staking/hooks' import styled from 'styled-components' import { TYPE } from 'theme' +import invariant from 'tiny-invariant' import { calcBoost } from 'utils/calcExpectedVeMobi' import Logo from '../../components/Logo' -import { useStablePoolInfo } from '../../state/stablePools/hooks' +import { CHAIN } from '../../constants' +import { useAllClaimableMobi } from '.' import ClaimAllMobiModal from './ClaimAllMobiModal' -import GaugeVoteModal from './GaugeVoteModal' const Container = styled.div` width: 100%; @@ -32,11 +34,7 @@ const Container = styled.div` width: 100% `} ` -const SmallButton = styled(ButtonOutlined)` - padding: 0.5rem; - width: 8rem; - border-color: ${({ theme }) => theme.primary1}; -` + const Wrapper = styled(AutoColumn)<{ showBackground: boolean; bgColor: any }>` width: 100%; overflow: hidden; @@ -63,30 +61,36 @@ const StyledLogo = styled(Logo)<{ size: string }>` box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075); background-color: ${({ theme }) => theme.white}; ` -const Divider = styled.div` - width: 100%; - height: 1px; - margin: 0; - background: ${({ theme }) => theme.bg4}; -` +function parse(include: boolean[], toParse: T[]): T[] { + invariant(include.length === toParse.length, 'length mismatch') + return toParse.filter((_, i) => include[i]) +} type PositionsProps = { - stakingInfo: MobiStakingInfo - unclaimedMobi: TokenAmount + stakingInfo: StakingInfo + userStakingInfo: UserStakingInfo + gauges: (GaugeInfo | null)[] + userGauges: (UserGaugeInfo | null)[] } -export default function Positions({ stakingInfo, unclaimedMobi }: PositionsProps) { - const { positions = [] } = stakingInfo - const loading = positions.length === 0 - const greaterThanZero = positions.filter( - ({ baseBalance, unclaimedMobi }) => baseBalance.greaterThan('0') || unclaimedMobi.greaterThan('0') + +export default function Positions({ stakingInfo, userStakingInfo, gauges, userGauges }: PositionsProps) { + const claimableMobi = useAllClaimableMobi(userGauges) + const openPosition = userGauges.map( + (ug) => ug !== null && (JSBI.greaterThan(ug.balance, JSBI.BigInt('0')) || ug.claimableMobi.greaterThan('0')) ) + const [openModal, setOpenModal] = useState(false) return ( setOpenModal(false)} - summaries={greaterThanZero.filter(({ unclaimedMobi }) => unclaimedMobi.greaterThan('0'))} + userGauges={ + parse(openPosition, userGauges).filter( + (ug) => ug !== null && ug.claimableMobi.greaterThan('0') + ) as UserGaugeInfo[] + } + gauges={parse(openPosition, StablePools[CHAIN]).map((d) => d.gauge as IGauge)} /> Your Positions @@ -96,24 +100,22 @@ export default function Positions({ stakingInfo, unclaimedMobi }: PositionsProps fontWeight={800} fontSize={[18, 24]} > - {unclaimedMobi.toSignificant(4)} Unclaimed MOBI + {claimableMobi.toSignificant(4)} Unclaimed MOBI - {loading ? ( - - - - ) : ( - greaterThanZero.map((position) => ( + {openPosition.map((p, i) => + p ? ( - )) + ) : null )} - {JSBI.greaterThan(unclaimedMobi.raw, JSBI.BigInt(0)) && ( + {claimableMobi.greaterThan('0') && ( setOpenModal(true)} style={{ fontWeight: 700, fontSize: 18, marginBottom: '1rem' }} @@ -126,29 +128,35 @@ export default function Positions({ stakingInfo, unclaimedMobi }: PositionsProps } function PositionCard({ - position, - votingPower, - totalVotingPower, + stakingInfo, + userStakingInfo, + gaugeInfo, + userGaugeInfo, + displayPool, }: { - position: GaugeSummary - votingPower: JSBI - totalVotingPower: JSBI + stakingInfo: StakingInfo + userStakingInfo: UserStakingInfo + gaugeInfo: GaugeInfo + userGaugeInfo: UserGaugeInfo + displayPool: DisplayPool }) { - const lpAsUsd = usePriceOfLp(position.poolAddress, position.baseBalance) - const [voteModalOpen, setVoteModalOpen] = useState(false) - const boost = calcBoost(position, votingPower, totalVotingPower) + const lpAsUsd = new TokenAmount(CUSD[CHAIN], '1') + const boost = calcBoost( + userGaugeInfo.balance, + gaugeInfo.totalSupply, + userStakingInfo.votingPower.raw, + stakingInfo.totalVotingPower.raw + ) - const stablePools = useStablePoolInfo() - const poolInfo = stablePools.filter((x) => x.name === position.pool)[0] - const poolColor = usePoolColor(poolInfo) + const poolColor = usePoolColor(displayPool) const [expand, setExpand] = useState(false) const mobileView = (
- - {position.pool} + + {displayPool.name} {expand ? : } @@ -169,28 +177,10 @@ function PositionCard({
)}
- // - // - // - // {position.pool} - // {`$${lpAsUsd?.toSignificant(4)}`} - // - // - // {`${boost.greaterThan(JSBI.BigInt(0)) ? boost.toFixed(2) : '1'}x`} - // - // ) return ( <> - setVoteModalOpen(false)} /> - setExpand(!expand)}> {isMobile ? ( @@ -198,8 +188,8 @@ function PositionCard({ ) : ( - - {position.pool} + + {displayPool.name} {` - $${lpAsUsd?.toSignificant(4)}`} el?.balance ?? JSBI.BigInt(0))) + const { avgApr } = useExternalStakingRewards() const isDarkMode = useIsDarkMode() const displayData = [ { label: 'Your Voting Power', - value: stakingInfo?.votingPower?.toSignificant(4, { groupSeparator: ',' }), + value: userStakingInfo?.votingPower?.toSignificant(4, { groupSeparator: ',' }), img: isDarkMode ? lockDark : lockLight, }, { label: 'Your total deposits', - value: priceOfDeposits ? '$' + priceOfDeposits?.toSignificant(4, { groupSeparator: ',' }) : undefined, + value: '$' + priceOfDeposits?.toSignificant(4, { groupSeparator: ',' }), img: isDarkMode ? cashDark : cashLight, }, { diff --git a/src/pages/Staking/VeMobiRewards.tsx b/src/pages/Staking/VeMobiRewards.tsx index f111b5cddb2..49e9c10776f 100644 --- a/src/pages/Staking/VeMobiRewards.tsx +++ b/src/pages/Staking/VeMobiRewards.tsx @@ -1,17 +1,20 @@ import { TransactionResponse } from '@ethersproject/abstract-provider' -import { ButtonEmpty, ButtonPrimary } from 'components/Button' +import { ButtonPrimary } from 'components/Button' import { AutoColumn } from 'components/Column' import Loader from 'components/Loader' import { RowBetween, RowFixed } from 'components/Row' +import { ExternalStakingRewards } from 'constants/staking' import { useWeb3Context } from 'hooks' import { useFeeDistributor, useStakingContract } from 'hooks/useContract' +import { useExternalStakingRewards, useUserExternalStakingRewards } from 'hooks/useExternalStakingRewards' import React, { useState } from 'react' -import { useFeeInformation, useSNXRewardInfo } from 'state/staking/hooks' +import { useFeeInfo } from 'state/staking/hooks' import { useTransactionAdder } from 'state/transactions/hooks' import styled from 'styled-components' import { TYPE } from 'theme' import { CardNoise, CardSection, DataCard } from '../../components/earn/styled' +import { CHAIN } from '../../constants' const VoteCard = styled(DataCard)` background: radial-gradient(90% 90% at 50% 5%, #3488ec 0%, #35d07f 100%); @@ -110,16 +113,13 @@ const Divider = styled.div` opacity: 0.5; ` -const StyledButton = styled(ButtonEmpty)` - margin-left: auto; - margin-right: 0.5rem; -` - export default function VeMobiRewards() { - const { rewardToken, rewardRate, avgApr, userRewardRate, leftToClaim, snxAddress } = useSNXRewardInfo() - const { totalFeesThisWeek, totalFeesNextWeek } = useFeeInformation() - const tokenColor = '#ab9325' //useColor(rewardToken) - const { connected, address: account } = useWeb3Context() + const { rewardRate, avgApr } = useExternalStakingRewards() + const { userRewardRate, claimableRewards } = useUserExternalStakingRewards() + const { totalFeesThisWeek, totalFeesNextWeek } = useFeeInfo() + const snxAddress = ExternalStakingRewards[CHAIN] + const tokenColor = '#ab9325' + const { connected, address } = useWeb3Context() const stakingContract = useStakingContract(snxAddress) const addTransaction = useTransactionAdder() const [attempting, setAttempting] = useState(false) @@ -147,9 +147,9 @@ export default function VeMobiRewards() { } async function claimFees() { - if (feeDistributorContract && account) { + if (feeDistributorContract && address) { setAttemptingFees(true) - await feeDistributorContract['claim(address)'](account) + await feeDistributorContract['claim(address)'](address) .then((response: TransactionResponse) => { addTransaction(response, { summary: `Claimed allocated Mobi fees`, @@ -194,7 +194,7 @@ export default function VeMobiRewards() { - {`${rewardToken.symbol} Rewards`} + {`${rewardRate.token.symbol} Rewards`} @@ -206,7 +206,7 @@ export default function VeMobiRewards() { - {`${rewardToken.symbol} Rate: `} + {`${rewardRate.token.symbol} Rate: `} @@ -215,7 +215,7 @@ export default function VeMobiRewards() { fontSize={[13, 16]} fontWeight={800} color={tokenColor} - >{`${rewardRate.toSignificant(4, { groupSeparator: ',' })} ${rewardToken.symbol} / WEEK`} + >{`${rewardRate.toSignificant(4, { groupSeparator: ',' })} ${rewardRate.token.symbol} / WEEK`} @@ -225,23 +225,22 @@ export default function VeMobiRewards() { - - {userRewardRate - ? `${userRewardRate?.toSignificant(4, { groupSeparator: ',' })} ${rewardToken.symbol} / WEEK` - : '--'} - + {`${userRewardRate.toSignificant(4, { groupSeparator: ',' })} ${ + rewardRate.token.symbol + } / WEEK`} <> - {/* - Claim - */} Available to Claim: - - {leftToClaim ? ( - {`${leftToClaim.toSignificant(4, { groupSeparator: ',' })} ${ - rewardToken.symbol + {claimableRewards ? ( + {`${claimableRewards.toSignificant(4, { groupSeparator: ',' })} ${ + rewardRate.token.symbol }`} ) : connected ? ( @@ -255,7 +254,7 @@ export default function VeMobiRewards() { disabled={attempting && !hash} style={{ fontWeight: 700, fontSize: 18, marginBottom: '1rem' }} > - {attempting && !hash ? `Claiming ${leftToClaim?.toFixed(2)} CELO...` : 'CLAIM'} + {attempting && !hash ? `Claiming ${claimableRewards?.toFixed(2)} CELO...` : 'CLAIM'} diff --git a/src/pages/Staking/Vote.tsx b/src/pages/Staking/Vote.tsx index 88ce255a635..151ef31d649 100644 --- a/src/pages/Staking/Vote.tsx +++ b/src/pages/Staking/Vote.tsx @@ -1,17 +1,19 @@ import { AutoColumn } from 'components/Column' -import Loader from 'components/Loader' import { RowFixed } from 'components/Row' -import { ChainLogo } from 'constants/StablePools' +import { ChainLogo, DisplayPool, StablePools } from 'constants/pools' import { usePoolColor } from 'hooks/useColor' +import { useVotePowerLeft } from 'hooks/useStaking' import React, { useState } from 'react' import { isMobile } from 'react-device-detect' -import { GaugeSummary, useVotePowerLeft } from 'state/staking/hooks' +import { GaugeInfo, UserGaugeInfo } from 'state/gauges/hooks' +import { UserStakingInfo } from 'state/staking/hooks' import styled from 'styled-components' import { TYPE } from 'theme' +import invariant from 'tiny-invariant' import CurrencyPoolLogo from '../../components/CurrencyPoolLogo' import Logo from '../../components/Logo' -import { useStablePoolInfo } from '../../state/stablePools/hooks' +import { CHAIN } from '../../constants' import GaugeVoteModal from './GaugeVoteModal' const Wrapper = styled(AutoColumn)` @@ -56,20 +58,17 @@ const TopSection = styled.div` `}; ` -interface GaugeWeightsProps { - summaries: GaugeSummary[] - lockDate: Date +interface VoteProps { + gauges: (GaugeInfo | null)[] + userGauges: (UserGaugeInfo | null)[] + userStaking: UserStakingInfo } -export default function Vote({ summaries, lockDate }: GaugeWeightsProps) { +export default function Vote({ gauges, userGauges, userStaking }: VoteProps) { const votePowerLeft = useVotePowerLeft() - const tooLateToVote = lockDate.valueOf() - Date.now() <= 7 * 24 * 60 * 60 * 1000 + const tooLateToVote = userStaking.lock.end.valueOf() - Date.now() <= 7 * 24 * 60 * 60 * 1000 - return summaries.length === 0 ? ( - - - - ) : ( + return ( Allocate your voting power to affect the MOBI distribution of each pool. {votePowerLeft}% Left to Allocate @@ -79,9 +78,20 @@ export default function Vote({ summaries, lockDate }: GaugeWeightsProps) { )} - {summaries.map((summary) => ( - - ))} + {gauges.map((g, i) => { + if (g === null) return null + const userGauge = userGauges[i] + invariant(userGauge, 'user gauge') + return ( + + ) + })} ) @@ -140,43 +150,50 @@ const SecondSection = styled.div<{ mobile: boolean }>` `}; ` -function WeightCard({ position, disabled }: { position: GaugeSummary; disabled: boolean }) { +function WeightCard({ + userGauge, + gauge, + displayPool, + disabled, +}: { + userGauge: UserGaugeInfo + gauge: GaugeInfo + displayPool: DisplayPool + disabled: boolean +}) { const [voteModalOpen, setVoteModalOpen] = useState(false) - const stablePools = useStablePoolInfo() - const poolInfo = stablePools.filter((x) => x.name === position.pool)[0] - const poolColor = usePoolColor(poolInfo) + const poolColor = usePoolColor(displayPool) - return ( + return displayPool.gauge === null ? null : ( <> setVoteModalOpen(false)} - disabled={poolInfo.isDisabled} /> !disabled && setVoteModalOpen(true)}> - {poolInfo.isDisabled ? 'BURN' : position.pool} + {displayPool.name} - {!poolInfo.isDisabled && ( - - )} + - {`Future: ${position.futureWeight.toFixed(2)}%`} + {`Future: ${gauge.futureWeight.toFixed(2)}%`} - + - {poolInfo.tokens.map((t) => t.symbol).join(' / ')} + {displayPool.pool.tokens.map((t) => t.symbol).join(' / ')}
@@ -185,22 +202,15 @@ function WeightCard({ position, disabled }: { position: GaugeSummary; disabled: fontSize={[13, 16]} fontWeight={800} color={poolColor} - >{`Current: ${position.currentWeight.toFixed(2)}%`} + >{`Current: ${gauge.weight.toFixed(2)}%`} {`My Vote: ${position.powerAllocated.toFixed(2)}%`} + >{`My Vote: ${userGauge.powerAllocated.toFixed(2)}%`}
- {poolInfo.isDisabled && ( - - - Any inflation allocated toward this gauge will be burnt. - - - )}
) diff --git a/src/pages/Staking/index.tsx b/src/pages/Staking/index.tsx index 26d2465dfb5..e1130095899 100644 --- a/src/pages/Staking/index.tsx +++ b/src/pages/Staking/index.tsx @@ -1,29 +1,20 @@ -import { TokenAmount } from '@ubeswap/sdk' import { useMobi } from 'hooks/Tokens' +import { TokenAmount } from 'lib/token-utils' import React from 'react' import { isMobile } from 'react-device-detect' -import { useMobiStakingInfo } from 'state/staking/hooks' +import { useAllGaugesInfo, useAllUserGaugesInfo, UserGaugeInfo } from 'state/gauges/hooks' +import { useStakingInfo, useUserStakingInfo } from 'state/staking/hooks' import styled from 'styled-components' import { Row } from '../../components/Row' import CalcBoost from './CalcBoost' -import { getAllUnclaimedMobi } from './ClaimAllMobiModal' import GaugeWeights from './GaugeWeights' +import Stake from './Lock/Stake' import Positions from './Positions' -import Stake from './Stake' import StatsHeader from './StatsHeader' import VeMobiRewards from './VeMobiRewards' import Vote from './Vote' -const TextContainer = styled.div` - width: 100%; - display: flex; - flex-wrap: wrap; - margin-left: auto; - margin-right: auto; - justify-content: center; -` - const PositionsContainer = styled.div` width: 100%; display: flex; @@ -47,15 +38,6 @@ const OuterContainer = styled.div` flex-direction: column; ` -const Divider = styled.div` - height: 1px; - width: 100%; - background: ${({ theme }) => theme.text1}; - margin-top: 1rem; - margin-bottom: 2rem; - opacity: 0.2; -` - const HeaderLinks = styled(Row)` justify-self: center; background-color: ${({ theme }) => theme.bg1}; @@ -94,16 +76,25 @@ enum View { Rewards = 4, } -export default function Staking() { - const stakingInfo = useMobiStakingInfo() +export const useAllClaimableMobi = (userGauges: (UserGaugeInfo | null)[]): TokenAmount => { const mobi = useMobi() - const unclaimedMobi = new TokenAmount(mobi, getAllUnclaimedMobi(stakingInfo.positions ?? [])) + return userGauges.reduce( + (accum, userGauge) => (userGauge ? accum.add(userGauge.claimableMobi) : accum), + new TokenAmount(mobi, '0') + ) +} + +export default function Staking() { + const userGauges = useAllUserGaugesInfo() + const gauges = useAllGaugesInfo() + const stakingInfo = useStakingInfo() + const userStakingInfo = useUserStakingInfo() const [view, setView] = React.useState(View.Lock) return ( - +
setView(View.Lock)} selected={view === View.Lock}> @@ -122,17 +113,27 @@ export default function Staking() {
{view === View.Lock ? ( - - - + + + ) : view === View.Vote ? ( - + ) : view === View.Analyze ? ( - + ) : ( diff --git a/src/pages/Swap/index.tsx b/src/pages/Swap/index.tsx index f071de98315..4af5dd5672d 100644 --- a/src/pages/Swap/index.tsx +++ b/src/pages/Swap/index.tsx @@ -1,11 +1,12 @@ -import { TokenAmount } from '@ubeswap/sdk' import { describeTrade } from 'components/swap/routing/describeTrade' import { useTradeCallback } from 'components/swap/routing/useTradeCallback' +import { TokenAmount } from 'lib/token-utils' import React, { useCallback, useContext, useEffect, useState } from 'react' import { isMobile } from 'react-device-detect' import { ArrowDown } from 'react-feather' import ReactGA from 'react-ga' import { Text } from 'rebass' +import { poolInfoToExchange } from 'state/mobiusPools/hooks' import { ThemeContext } from 'styled-components' import invariant from 'tiny-invariant' @@ -46,14 +47,7 @@ export default function Swap() { // swap state const { independentField, typedValue } = useSwapState() - const { - v2Trade, - currencyBalances, - parsedAmount, - currencies, - inputError: swapInputError, - priceImpact, - } = useMobiusTradeInfo() + const { v2Trade, currencyBalances, parsedAmount, currencies, inputError: swapInputError } = useMobiusTradeInfo() const trade = v2Trade @@ -100,7 +94,10 @@ export default function Swap() { } // check whether the user has approved the router on the input token - const [approval, approveCallback] = useApproveCallback(trade?.input, trade?.pool.address) + const [approval, approveCallback] = useApproveCallback( + trade?.input, + trade?.pool ? poolInfoToExchange(trade?.pool).address : undefined + ) // check if user has gone through approval process, used to show two step buttons, reset on token change const [approvalSubmitted, setApprovalSubmitted] = useState(false) @@ -128,7 +125,7 @@ export default function Swap() { ReactGA.event({ category: 'Swap', action: 'Swap', - label: [trade?.input?.currency?.symbol, trade?.output?.currency?.symbol].join('/'), + label: [trade?.input?.token.symbol, trade?.output?.token.symbol].join('/'), }) }) .catch((error) => { @@ -146,7 +143,7 @@ export default function Swap() { const [showInverted, setShowInverted] = useState(false) // warnings on slippage - const priceImpactSeverity = warningSeverity(priceImpact) + const priceImpactSeverity = warningSeverity(v2Trade?.priceImpact) // show approve flow when: no error on inputs, not approved or pending, or approved in current session // never show if price impact is above threshold in non expert mode @@ -176,9 +173,13 @@ export default function Swap() { [onCurrencySelection] ) - const handleMaxInput = useCallback(() => { - maxAmountInput && onUserInput(Field.INPUT, maxAmountInput.toExact()) - }, [maxAmountInput, onUserInput]) + const handleMaxInput = useCallback( + (amount?: TokenAmount) => { + ;(amount && onUserInput(Field.INPUT, amount.toExact())) || + (maxAmountInput && onUserInput(Field.INPUT, maxAmountInput.toExact())) + }, + [maxAmountInput, onUserInput] + ) const handleOutputSelect = useCallback( (outputCurrency) => onCurrencySelection(Field.OUTPUT, outputCurrency), diff --git a/src/pages/Swap/redirects.tsx b/src/pages/Swap/redirects.tsx deleted file mode 100644 index 48680c38d42..00000000000 --- a/src/pages/Swap/redirects.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React, { useEffect } from 'react' -import { useDispatch } from 'react-redux' -import { Redirect, RouteComponentProps } from 'react-router-dom' - -import { AppDispatch } from '../../state' -import { ApplicationModal, setOpenModal } from '../../state/application/actions' - -// Redirects to swap but only replace the pathname -export function RedirectPathToSwapOnly({ location }: RouteComponentProps) { - return -} - -// Redirects from the /swap/:outputCurrency path to the /swap?outputCurrency=:outputCurrency format -export function RedirectToSwap(props: RouteComponentProps<{ outputCurrency: string }>) { - const { - location: { search }, - match: { - params: { outputCurrency }, - }, - } = props - - return ( - 1 - ? `${search}&outputCurrency=${outputCurrency}` - : `?outputCurrency=${outputCurrency}`, - }} - /> - ) -} - -export function OpenClaimAddressModalAndRedirectToSwap(props: RouteComponentProps) { - const dispatch = useDispatch() - useEffect(() => { - dispatch(setOpenModal(ApplicationModal.ADDRESS_CLAIM)) - }, [dispatch]) - return -} diff --git a/src/pages/Vote/VotePage.tsx b/src/pages/Vote/VotePage.tsx index 991b1ed140d..974473d1aa8 100644 --- a/src/pages/Vote/VotePage.tsx +++ b/src/pages/Vote/VotePage.tsx @@ -1,9 +1,9 @@ import { BigNumber } from '@ethersproject/bignumber' -// eslint-disable-next-line no-restricted-imports -import { TokenAmount } from '@ubeswap/sdk' import { CardNoise } from 'components/claim/styled' import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp' import JSBI from 'jsbi' +// eslint-disable-next-line no-restricted-imports +import { TokenAmount } from 'lib/token-utils' import { DateTime } from 'luxon/src/luxon' import React, { useState } from 'react' import { ArrowLeft } from 'react-feather' diff --git a/src/pages/Vote/index.tsx b/src/pages/Vote/index.tsx index 03eed2de656..b6b5814b5b5 100644 --- a/src/pages/Vote/index.tsx +++ b/src/pages/Vote/index.tsx @@ -1,4 +1,3 @@ -import { TokenAmount } from '@ubeswap/sdk' import { ButtonPrimary } from 'components/Button' import { CardNoise } from 'components/claim/styled' import { AutoColumn } from 'components/Column' @@ -8,6 +7,7 @@ import Loader from 'components/Loader' import { AutoRow, RowBetween } from 'components/Row' import ProposalEmptyState from 'components/vote/ProposalEmptyState' import { useWeb3Context } from 'hooks' +import { TokenAmount } from 'lib/token-utils' import { darken } from 'polished' import React from 'react' import { Link } from 'react-router-dom' diff --git a/src/state/application/actions.ts b/src/state/application/actions.ts index 82ef3a8a434..acac85e2414 100644 --- a/src/state/application/actions.ts +++ b/src/state/application/actions.ts @@ -1,5 +1,4 @@ import { createAction } from '@reduxjs/toolkit' -import { Fraction } from '@ubeswap/sdk' export type PopupContent = { txn: { @@ -12,11 +11,7 @@ export type PopupContent = { export enum ApplicationModal { WALLET, SETTINGS, - SELF_CLAIM, - ADDRESS_CLAIM, - CLAIM_POPUP, MENU, - DELEGATE, VOTE, } @@ -25,6 +20,6 @@ export const setOpenModal = createAction('application/s export const addPopup = createAction<{ key?: string; removeAfterMs?: number | null; content: PopupContent }>('application/addPopup') export const removePopup = createAction<{ key: string }>('application/removePopup') -export const btcEthPrice = createAction<{ ethPrice: string; btcPrice: string }>('application/btcEthPrice') -export const addPrice = createAction<{ token: string; price: Fraction }>('application/addPrice') + +export const addPrice = createAction<{ token: string; price: string }>('application/addPrice') export const addPrices = createAction<{ prices: { [address: string]: string } }>('application/addPrices') diff --git a/src/state/application/hooks.ts b/src/state/application/hooks.ts index 3a6203e50df..94b510d0a02 100644 --- a/src/state/application/hooks.ts +++ b/src/state/application/hooks.ts @@ -1,6 +1,7 @@ -import { useContractKit } from '@celo-tools/use-contractkit' -import { Fraction } from '@ubeswap/sdk' -import JSBI from 'jsbi' +import { ApolloClient, NormalizedCacheObject } from '@apollo/client' +import { useWeb3Context } from 'hooks' +import { useMobi } from 'hooks/Tokens' +import { Fraction } from 'lib/token-utils' import { useCallback, useMemo } from 'react' import { useDispatch, useSelector } from 'react-redux' @@ -34,35 +35,23 @@ export function useCloseModals(): () => void { } export function useWalletModalToggle(): () => void { - const { connect, address } = useContractKit() + const { connect, connected } = useWeb3Context() const toggle = useToggleModal(ApplicationModal.WALLET) - return address === null ? connect : toggle + return !connected ? connect : toggle } export function useToggleSettingsMenu(): () => void { return useToggleModal(ApplicationModal.SETTINGS) } -export function useShowClaimPopup(): boolean { - return useModalOpen(ApplicationModal.CLAIM_POPUP) -} - -export function useToggleShowClaimPopup(): () => void { - return useToggleModal(ApplicationModal.CLAIM_POPUP) -} - -export function useToggleSelfClaimModal(): () => void { - return useToggleModal(ApplicationModal.SELF_CLAIM) -} - -export function useToggleDelegateModal(): () => void { - return useToggleModal(ApplicationModal.DELEGATE) -} - export function useToggleVoteModal(): () => void { return useToggleModal(ApplicationModal.VOTE) } +export function useUbeswapClient(): ApolloClient { + return useSelector>((state) => state.application.ubeswapClient) +} + // returns a function that allows adding a popup export function useAddPopup(): (content: PopupContent, key?: string) => void { const dispatch = useDispatch() @@ -92,32 +81,31 @@ export function useActivePopups(): AppState['application']['popupList'] { return useMemo(() => list.filter((item) => item.show), [list]) } -export function useEthBtcPrice(address: string): JSBI { - const prices = useSelector((state: AppState) => ({ - ethPrice: state.application.ethPrice, - btcPrice: state.application.btcPrice, - })) - return address === '0x19260b9b573569dDB105780176547875fE9fedA3' || - address === '0xBe50a3013A1c94768A1ABb78c3cB79AB28fc1aCE' - ? JSBI.BigInt(prices.btcPrice) - : address === '0xE0F2cc70E52f05eDb383313393d88Df2937DA55a' || - address === '0xE919F65739c26a42616b7b8eedC6b5524d1e3aC4' - ? JSBI.BigInt(prices.ethPrice) - : JSBI.BigInt('1') +export function useTokenPrice(address: string | undefined): Fraction | undefined { + const prices = useTokenPrices() + if (!address) return undefined + + return priceStringToFraction(prices[address]) +} + +export function usePegPrice(query: string | null): Fraction | undefined { + const prices = useTokenPrices() + if (query === null) return new Fraction(1) + + return priceStringToFraction(prices[query]) } -export function useTokenPrice(address: string | undefined): Fraction | undefined { - const priceString = useSelector((state: AppState) => { - return state.application.tokenPrices[address?.toLowerCase()] - }) - return priceStringToFraction(priceString) +export function useMobiPrice(): Fraction { + const mobi = useMobi() + const prices = useTokenPrices() + return priceStringToFraction(prices[mobi.address.toLowerCase()]) ?? new Fraction('0') } export function priceStringToFraction(priceString: string | undefined): Fraction | undefined { if (!priceString) return undefined const price = parseFloat(priceString) * 10 ** 4 - const asFraction = new Fraction(price.toFixed(0), '10000') - return asFraction ?? undefined + const asFraction = new Fraction(price.toFixed(0), 10000) + return asFraction } export function useTokenPrices() { diff --git a/src/state/application/reducer.test.ts b/src/state/application/reducer.test.ts deleted file mode 100644 index ed494e7fd56..00000000000 --- a/src/state/application/reducer.test.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { ChainId } from '@ubeswap/sdk' -import { createStore, Store } from 'redux' - -import { addPopup, ApplicationModal, removePopup, setOpenModal, updateBlockNumber } from './actions' -import reducer, { ApplicationState } from './reducer' - -describe('application reducer', () => { - let store: Store - - beforeEach(() => { - store = createStore(reducer, { - popupList: [], - blockNumber: { - [ChainId.MAINNET]: 3, - }, - openModal: null, - }) - }) - - describe('addPopup', () => { - it('adds the popup to list with a generated id', () => { - store.dispatch(addPopup({ content: { txn: { hash: 'abc', summary: 'test', success: true } } })) - const list = store.getState().popupList - expect(list).toHaveLength(1) - expect(typeof list[0].key).toEqual('string') - expect(list[0].show).toEqual(true) - expect(list[0].content).toEqual({ txn: { hash: 'abc', summary: 'test', success: true } }) - expect(list[0].removeAfterMs).toEqual(15000) - }) - - it('replaces any existing popups with the same key', () => { - store.dispatch(addPopup({ key: 'abc', content: { txn: { hash: 'abc', summary: 'test', success: true } } })) - store.dispatch(addPopup({ key: 'abc', content: { txn: { hash: 'def', summary: 'test2', success: false } } })) - const list = store.getState().popupList - expect(list).toHaveLength(1) - expect(list[0].key).toEqual('abc') - expect(list[0].show).toEqual(true) - expect(list[0].content).toEqual({ txn: { hash: 'def', summary: 'test2', success: false } }) - expect(list[0].removeAfterMs).toEqual(15000) - }) - }) - - describe('setOpenModal', () => { - it('set wallet modal', () => { - store.dispatch(setOpenModal(ApplicationModal.WALLET)) - expect(store.getState().openModal).toEqual(ApplicationModal.WALLET) - store.dispatch(setOpenModal(ApplicationModal.WALLET)) - expect(store.getState().openModal).toEqual(ApplicationModal.WALLET) - store.dispatch(setOpenModal(ApplicationModal.CLAIM_POPUP)) - expect(store.getState().openModal).toEqual(ApplicationModal.CLAIM_POPUP) - store.dispatch(setOpenModal(null)) - expect(store.getState().openModal).toEqual(null) - }) - }) - - describe('updateBlockNumber', () => { - it('updates block number', () => { - store.dispatch(updateBlockNumber({ chainId: ChainId.MAINNET, blockNumber: 4 })) - expect(store.getState().blockNumber[ChainId.MAINNET]).toEqual(4) - }) - it('no op if late', () => { - store.dispatch(updateBlockNumber({ chainId: ChainId.MAINNET, blockNumber: 2 })) - expect(store.getState().blockNumber[ChainId.MAINNET]).toEqual(3) - }) - it('works with non-set chains', () => { - store.dispatch(updateBlockNumber({ chainId: ChainId.ALFAJORES, blockNumber: 2 })) - expect(store.getState().blockNumber).toEqual({ - [ChainId.MAINNET]: 3, - [ChainId.ALFAJORES]: 2, - }) - }) - }) - - describe('removePopup', () => { - beforeEach(() => { - store.dispatch(addPopup({ key: 'abc', content: { txn: { hash: 'abc', summary: 'test', success: true } } })) - }) - it('hides the popup', () => { - expect(store.getState().popupList[0].show).toBe(true) - store.dispatch(removePopup({ key: 'abc' })) - expect(store.getState().popupList).toHaveLength(1) - expect(store.getState().popupList[0].show).toBe(false) - }) - }) -}) diff --git a/src/state/application/reducer.ts b/src/state/application/reducer.ts index 5f2c0eb0845..25012f589bf 100644 --- a/src/state/application/reducer.ts +++ b/src/state/application/reducer.ts @@ -6,7 +6,6 @@ import { addPrice, addPrices, ApplicationModal, - btcEthPrice, PopupContent, removePopup, setOpenModal, @@ -14,6 +13,7 @@ import { } from './actions' type PopupList = Array<{ key: string; show: boolean; content: PopupContent; removeAfterMs: number | null }> + export type TokenPrices = { [address: string]: string } @@ -22,8 +22,6 @@ export interface ApplicationState { readonly blockNumber: { readonly [chainId: number]: number } readonly popupList: PopupList readonly openModal: ApplicationModal | null - readonly btcPrice: string - readonly ethPrice: string readonly tokenPrices: TokenPrices readonly ubeswapClient: ApolloClient } @@ -32,11 +30,9 @@ const initialState: ApplicationState = { blockNumber: {}, popupList: [], openModal: null, - btcPrice: '41000', - ethPrice: '2700', tokenPrices: {}, ubeswapClient: new ApolloClient({ - uri: 'https://api.thegraph.com/subgraphs/name/ubeswap/ubeswap', + uri: 'https://api.thegraph.com/subgraphs/name/ubeswap/ubeswap-backup', cache: new InMemoryCache(), }), } @@ -54,10 +50,6 @@ export default createReducer(initialState, (builder) => .addCase(setOpenModal, (state, action) => { state.openModal = action.payload }) - .addCase(btcEthPrice, (state, { payload: { ethPrice, btcPrice } }) => { - state.ethPrice = ethPrice - state.btcPrice = btcPrice - }) .addCase(addPopup, (state, { payload: { content, key, removeAfterMs = 15000 } }) => { state.popupList = (key ? state.popupList.filter((popup) => popup.key !== key) : state.popupList).concat([ { @@ -82,6 +74,9 @@ export default createReducer(initialState, (builder) => } }) .addCase(addPrices, (state, { payload: { prices } }) => { - state.tokenPrices = prices + state.tokenPrices = { + ...state.tokenPrices, + ...prices, + } }) ) diff --git a/src/state/application/updater.ts b/src/state/application/updater.ts index 95364bb6b29..9638b1c0183 100644 --- a/src/state/application/updater.ts +++ b/src/state/application/updater.ts @@ -1,31 +1,45 @@ -import { ApolloClient, gql, InMemoryCache, useQuery } from '@apollo/client' +import { gql, useQuery } from '@apollo/client' import axios from 'axios' +import { StablePools } from 'constants/pools' import { useWeb3Context } from 'hooks' -import { useCallback, useEffect, useState } from 'react' +import { useMobi } from 'hooks/Tokens' +import { Dispatch, useCallback, useEffect, useState } from 'react' import { useDispatch } from 'react-redux' import { CHAIN } from '../../constants' import useDebounce from '../../hooks/useDebounce' import useIsWindowVisible from '../../hooks/useIsWindowVisible' -import { addPrices, btcEthPrice, updateBlockNumber } from './actions' +import { addPrice, addPrices, updateBlockNumber } from './actions' +import { useUbeswapClient } from './hooks' +import { TokenPrices } from './reducer' -const fetchEthBtcPrices = async (dispatch: any) => { +const dedupe = (strings: string[]): string[] => { + const seen = new Set() + return strings.filter((str) => { + if (seen.has(str)) { + return false + } else { + seen.add(str) + return true + } + }) +} + +const fetchPegPrices = async (dispatch: Dispatch) => { + const pegQueries = dedupe(StablePools[CHAIN].map(({ peg }) => peg.priceQuery).filter((s) => s !== null) as string[]) + const ids = pegQueries.reduce((acc, cur) => acc.concat(cur).concat('%2'), '') const resp = await axios.get( - 'https://api.coingecko.com/api/v3/simple/token_price/ethereum?contract_addresses=0x2260fac5e5542a773aa44fbcfedf7c193bc2c599%2C0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2&vs_currencies=usd' + `https://api.coingecko.com/api/v3/simple/price?ids=${ids.slice(0, -2)}&vs_currencies=usd` ) - const btcPrice: string = resp.data['0x2260fac5e5542a773aa44fbcfedf7c193bc2c599']?.['usd'] - const ethPrice: string = resp.data['0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2']?.['usd'] - dispatch(btcEthPrice({ btcPrice: parseInt(btcPrice).toFixed(0), ethPrice: parseInt(ethPrice).toFixed(0) })) + const prices: TokenPrices = pegQueries.reduce((acc, cur) => { + return { ...acc, [cur]: (resp.data[cur]?.['usd'] as number).toString() } + }, {}) + dispatch(addPrices({ prices: prices })) } -// 0x17700282592d6917f6a73d0bf8accf4d578c131e -const ubeswapClient = new ApolloClient({ - uri: 'https://api.thegraph.com/subgraphs/name/ubeswap/ubeswap-backup', - cache: new InMemoryCache(), -}) -const priceQuery = gql` +const mobiPriceQuery = gql` { - tokens(where: { derivedCUSD_gt: "0" }) { + token(id: "0x73a210637f6f6b7005512677ba6b3c96bb4aa44b") { id derivedCUSD } @@ -34,15 +48,16 @@ const priceQuery = gql` export function PriceData(): null { const dispatch = useDispatch() - const { data, loading, error } = useQuery(priceQuery, { client: ubeswapClient }) + const ubeswapClient = useUbeswapClient() + const mobi = useMobi() + const { data, loading, error } = useQuery(mobiPriceQuery, { client: ubeswapClient }) useEffect(() => { if (!loading && !error && data) { - const prices: { [address: string]: string } = data.tokens.reduce((accum, cur) => { - return { ...accum, [cur.id.toLowerCase()]: cur.derivedCUSD } - }, {}) - dispatch(addPrices({ prices })) + console.log('price update') + fetchPegPrices(dispatch) + dispatch(addPrice({ token: mobi.address.toLowerCase(), price: data.token.derivedCUSD })) } - }, [data, loading, dispatch, error]) + }, [data, loading, dispatch, error, mobi.address]) return null } @@ -55,7 +70,6 @@ export default function Updater(): null { chainId: CHAIN, blockNumber: null, }) - fetchEthBtcPrices(dispatch) const blockNumberCallback = useCallback( (blockNumber: number) => { @@ -91,6 +105,7 @@ export default function Updater(): null { useEffect(() => { if (!debouncedState.chainId || !debouncedState.blockNumber || !windowVisible) return + console.log('block update') dispatch(updateBlockNumber({ chainId: debouncedState.chainId, blockNumber: debouncedState.blockNumber })) }, [windowVisible, dispatch, debouncedState.blockNumber, debouncedState.chainId]) diff --git a/src/state/burn/actions.ts b/src/state/burn/actions.ts deleted file mode 100644 index e746699fb72..00000000000 --- a/src/state/burn/actions.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { createAction } from '@reduxjs/toolkit' - -export enum Field { - LIQUIDITY_PERCENT = 'LIQUIDITY_PERCENT', - LIQUIDITY = 'LIQUIDITY', - CURRENCY_A = 'CURRENCY_A', - CURRENCY_B = 'CURRENCY_B', -} - -export const typeInput = createAction<{ field: Field; typedValue: string }>('burn/typeInputBurn') diff --git a/src/state/burn/reducer.ts b/src/state/burn/reducer.ts deleted file mode 100644 index 188ff4e46bd..00000000000 --- a/src/state/burn/reducer.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { createReducer } from '@reduxjs/toolkit' - -import { Field, typeInput } from './actions' - -export interface BurnState { - readonly independentField: Field - readonly typedValue: string -} - -const initialState: BurnState = { - independentField: Field.LIQUIDITY_PERCENT, - typedValue: '0', -} - -export default createReducer(initialState, (builder) => - builder.addCase(typeInput, (state, { payload: { field, typedValue } }) => { - return { - ...state, - independentField: field, - typedValue, - } - }) -) diff --git a/src/state/claim/hooks.ts b/src/state/claim/hooks.ts index 382caa6471e..742a303de38 100644 --- a/src/state/claim/hooks.ts +++ b/src/state/claim/hooks.ts @@ -1,29 +1,23 @@ // To-Do: Implement Hooks to update Client-Side contract representation -import { JSBI } from '@ubeswap/sdk' +import { useMobi } from 'hooks/Tokens' +import { TokenAmount } from 'lib/token-utils' import { useSelector } from 'react-redux' import { AppState } from '..' import { Claim, VestType } from './reducer' export interface ClaimInfo { - readonly allocatedAmount: JSBI - readonly claimedAmount: JSBI - readonly unclaimedAmount: JSBI + readonly allocatedAmount: TokenAmount + readonly claimedAmount: TokenAmount + readonly unclaimedAmount: TokenAmount } export function useClaimInfo(type = VestType.LP): ClaimInfo { const claim = useSelector((state) => state.claim[type]) - - if (claim == null) { - return { - allocatedAmount: JSBI.BigInt(0), - claimedAmount: JSBI.BigInt(0), - unclaimedAmount: JSBI.BigInt(0), - } - } + const mobi = useMobi() return { - allocatedAmount: claim.allocatedAmount, - claimedAmount: claim.claimedAmount, - unclaimedAmount: claim.unclaimedAmount, + allocatedAmount: new TokenAmount(mobi, claim.allocatedAmount), + claimedAmount: new TokenAmount(mobi, claim.claimedAmount), + unclaimedAmount: new TokenAmount(mobi, claim.unclaimedAmount), } } diff --git a/src/state/claim/updater.ts b/src/state/claim/updater.ts index 5d455f4432e..e8ea8def5f3 100644 --- a/src/state/claim/updater.ts +++ b/src/state/claim/updater.ts @@ -1,16 +1,9 @@ -import { JSBI } from '@ubeswap/sdk' -import { useEffect } from 'react' import { useDispatch } from 'react-redux' -import { CHAIN } from '../../constants' -import { VestingAddresses } from '../../constants/StablePools' -import { VestingEscrow } from '../../generated' import { useWeb3Context } from '../../hooks' import { useVestingContract } from '../../hooks/useContract' import useCurrentBlockTimestamp from '../../hooks/useCurrentBlockTimestamp' import { AppDispatch } from '../index' -import { update } from './actions' -import { VestType } from './reducer' export default function UpdateClaim(): null { const { address, connected, provider } = useWeb3Context() @@ -19,31 +12,32 @@ export default function UpdateClaim(): null { const claimContract = useVestingContract() // automatically update lists if versions are minor/patch - useEffect(() => { - const updateClaim = async (vesting: VestingEscrow | undefined, type: VestType) => { - if (!vesting || !connected) return - const initialLocked = JSBI.BigInt(await vesting?.['initial_locked'](address)) - const unclaimed = JSBI.BigInt(await vesting?.['balanceOf'](address)) - const claimed = JSBI.subtract( - JSBI.subtract(initialLocked, JSBI.BigInt(await vesting?.['lockedOf'](address))), - unclaimed - ) - dispatch( - update({ - type, - claim: { - allocatedAmount: initialLocked, - claimedAmount: claimed, - unclaimedAmount: unclaimed, - }, - }) - ) - } - Object.entries(VestingAddresses).forEach(([type, addresses]) => { - const vestingContract = claimContract?.attach(addresses[CHAIN]) - updateClaim(vestingContract, parseInt(type)) - }) - }, [provider, blockNumber, claimContract, dispatch, connected, address]) + // useEffect(() => { + // const updateClaim = async (vesting: VestingEscrow | undefined, type: VestType) => { + // if (!vesting || !connected) return + // const initialLocked = JSBI.BigInt(await vesting?.['initial_locked'](address)) + // const unclaimed = JSBI.BigInt(await vesting?.['balanceOf'](address)) + // const claimed = JSBI.subtract( + // JSBI.subtract(initialLocked, JSBI.BigInt(await vesting?.['lockedOf'](address))), + // unclaimed + // ) + // console.log('claim update') + // dispatch( + // update({ + // type, + // claim: { + // allocatedAmount: initialLocked, + // claimedAmount: claimed, + // unclaimedAmount: unclaimed, + // }, + // }) + // ) + // } + // Object.entries(VestingAddresses).forEach(([type, addresses]) => { + // const vestingContract = claimContract?.attach(addresses[CHAIN]) + // updateClaim(vestingContract, parseInt(type)) + // }) + // }, [provider, blockNumber, claimContract, dispatch, connected, address]) return null } diff --git a/src/state/gauges/actions.ts b/src/state/gauges/actions.ts new file mode 100644 index 00000000000..0e309298188 --- /dev/null +++ b/src/state/gauges/actions.ts @@ -0,0 +1,6 @@ +import { createAction } from '@reduxjs/toolkit' + +import { IGaugeState, IUserGaugeState } from './reducer' + +export const updateGauges = createAction<{ gaugeState: (IGaugeState | null)[] }>('gauges/update') +export const updateGaugesUser = createAction<{ userGaugeState: (IUserGaugeState | null)[] }>('gauges/updateUser') diff --git a/src/state/gauges/hooks.ts b/src/state/gauges/hooks.ts new file mode 100644 index 00000000000..a009315c806 --- /dev/null +++ b/src/state/gauges/hooks.ts @@ -0,0 +1,138 @@ +import { invariant } from '@apollo/client/utilities/globals' +import { IGauge, StablePools } from 'constants/pools' +import { useWeb3Context } from 'hooks' +import { useMobi } from 'hooks/Tokens' +import { useLiquidityGaugeContract } from 'hooks/useContract' +import JSBI from 'jsbi' +import { Percent, TokenAmount } from 'lib/token-utils' +import { useSelector } from 'react-redux' +import { useSingleContractMultipleData } from 'state/multicall/hooks' + +import { CHAIN, ZERO_ADDRESS } from '../../constants' +import { AppState } from '..' +import { IGaugeState, IUserGaugeState } from './reducer' + +export function useGauges(): readonly (IGaugeState | null)[] { + return useSelector((state) => state.gauges.gauges) +} + +export function useUserGauges(): readonly (IUserGaugeState | null)[] { + return useSelector((state) => state.gauges.gauges) +} + +export type GaugeInfo = { + isKilled: boolean + lastClaim: Date + weight: Percent + futureWeight: Percent + totalSupply: JSBI + workingSupply: JSBI + totalEffectiveBalance: JSBI +} + +export type UserGaugeInfo = { + balance: JSBI + claimableMobi: TokenAmount + lastVote: number + powerAllocated: number + effectiveBalance: JSBI +} + +export function useAllGaugesInfo(): (GaugeInfo | null)[] { + const gauges = useGauges() + + return gauges.map((g) => + !g + ? null + : { + ...g, + lastClaim: new Date(g.lastClaim), + weight: new Percent(g.weight), + futureWeight: new Percent(g.futureWeight), + } + ) +} + +export function useAllUserGaugesInfo(): (UserGaugeInfo | null)[] { + const userGauges = useUserGauges() + + const mobi = useMobi() + + return userGauges.map((ug) => + !ug + ? null + : { + ...ug, + claimableMobi: new TokenAmount(mobi, ug.claimableMobi), + } + ) +} + +export function useGaugeInfo(gauge: IGauge | undefined): GaugeInfo | undefined { + const gauges = useGauges() + + if (!gauge) return undefined + + const gaugeInfo = gauges + .filter( + (g, i) => + StablePools[CHAIN][i].gauge && (StablePools[CHAIN][i].gauge?.address === gauge.address.toLowerCase() ?? false) + ) + .map((g) => + !g + ? null + : { + ...g, + lastClaim: new Date(g.lastClaim), + weight: new Percent(g.weight), + futureWeight: new Percent(g.futureWeight), + } + ) + invariant(gaugeInfo.length === 1, 'duplicate gauges') + invariant(!!gaugeInfo[0], 'gauge not found') + return gaugeInfo[0] +} + +export function useUserGaugeInfo(gauge: IGauge | undefined): UserGaugeInfo | undefined { + const gauges = useUserGauges() + const mobi = useMobi() + + if (!gauge) return undefined + + const gaugeInfo = gauges + .filter( + (ug, i) => + StablePools[CHAIN][i].gauge && (StablePools[CHAIN][i].gauge?.address === gauge.address.toLowerCase() ?? false) + ) + .map((ug) => + !ug + ? null + : { + ...ug, + claimableMobi: new TokenAmount(mobi, ug.claimableMobi), + } + ) + invariant(gaugeInfo.length === 1, 'duplicate gauges') + invariant(!!gaugeInfo[0], 'gauge not found') + return gaugeInfo[0] +} + +export function useExternalRewards(gauge: IGauge | undefined | null): TokenAmount[] | undefined { + const gaugeContract = useLiquidityGaugeContract(gauge?.address ?? ZERO_ADDRESS) + const { address: userAddress, connected } = useWeb3Context() + + const claimableTokens = useSingleContractMultipleData( + gaugeContract, + 'claimable_reward_write', + gauge?.additionalRewards.map((token) => [ + connected ? userAddress : ZERO_ADDRESS, + token.token.address ?? ZERO_ADDRESS, + ]) ?? [] + ) + + if (!gauge) return undefined + + return claimableTokens?.map((result, i) => { + return new TokenAmount(gauge.additionalRewards[i].token, JSBI.BigInt(result.result?.[0] ?? '0')) + }) +} diff --git a/src/state/gauges/reducer.ts b/src/state/gauges/reducer.ts new file mode 100644 index 00000000000..8bdaa7b67e4 --- /dev/null +++ b/src/state/gauges/reducer.ts @@ -0,0 +1,75 @@ +import { createReducer } from '@reduxjs/toolkit' +import { IGauge, StablePools } from 'constants/pools' +import JSBI from 'jsbi' + +import { CHAIN } from '../../constants' +import { updateGauges, updateGaugesUser } from './actions' + +export interface Gauges { + readonly gauges: ((IUserGaugeState & IGaugeState) | null)[] +} + +export interface IGaugeState { + isKilled: boolean + lastClaim: number + weight: JSBI + futureWeight: JSBI + totalSupply: JSBI + workingSupply: JSBI + totalEffectiveBalance: JSBI +} + +export interface IUserGaugeState { + balance: JSBI + claimableMobi: JSBI + lastVote: number + powerAllocated: number + effectiveBalance: JSBI +} + +const initialGaugeInfo: IGaugeState = { + isKilled: false, + lastClaim: 0, + weight: JSBI.BigInt('0'), + futureWeight: JSBI.BigInt('0'), + totalSupply: JSBI.BigInt(0), + workingSupply: JSBI.BigInt(0), + totalEffectiveBalance: JSBI.BigInt(0), +} + +const initialUserGaugeInfo: IUserGaugeState = { + balance: JSBI.BigInt(0), + claimableMobi: JSBI.BigInt(0), + lastVote: 0, + powerAllocated: 0, + effectiveBalance: JSBI.BigInt(0), +} + +function emptyExchangeInfo(gauge: IGauge | null): (IGaugeState & IUserGaugeState) | null { + return gauge === null + ? null + : { + ...initialGaugeInfo, + ...initialUserGaugeInfo, + } +} + +const initialState: Gauges = { + gauges: StablePools[CHAIN].map((p) => emptyExchangeInfo(p.gauge)), +} + +export default createReducer(initialState, (builder) => + builder + .addCase(updateGauges, (state, { payload: { gaugeState } }) => { + const r = state.gauges.map((g, i) => (g ? { ...g, ...gaugeState[i] } : null)) + return { + gauges: r, + } + }) + .addCase(updateGaugesUser, (state, { payload: { userGaugeState } }) => { + const r = state.gauges.map((g, i) => (g ? { ...g, ...userGaugeState[i] } : null)) + return { + gauges: r, + } + }) +) diff --git a/src/state/gauges/updater.ts b/src/state/gauges/updater.ts new file mode 100644 index 00000000000..db9fe388621 --- /dev/null +++ b/src/state/gauges/updater.ts @@ -0,0 +1,115 @@ +import { Interface } from '@ethersproject/abi' +import { IGauge, StablePools } from 'constants/pools' +import { useWeb3Context } from 'hooks' +import { useDispatch } from 'react-redux' +import { AppDispatch } from 'state' +import { useBlockNumber } from 'state/application/hooks' +import { useMultipleContractSingleData, useSingleContractMultipleData } from 'state/multicall/hooks' + +import { CHAIN } from '../../constants' +import GAUGE_V3 from '../../constants/abis/LiquidityGaugeV3.json' +import { useGaugeControllerContract } from '../../hooks/useContract' + +const gaugeInterface = new Interface(GAUGE_V3.abi) + +export function GaugeUpdater() { + const { address, connected } = useWeb3Context() + const blockNumber = useBlockNumber() + const dispatch = useDispatch() + const gauges: (IGauge | null)[] = StablePools[CHAIN].map((display) => display.gauge) + const gaugeAddresses = gauges.map((g) => g?.address) + const gaugeController = useGaugeControllerContract() + + const totalSupply = useMultipleContractSingleData(gaugeAddresses, gaugeInterface, 'totalSupply') + const workingSupply = useMultipleContractSingleData(gaugeAddresses, gaugeInterface, 'working_supply') + const totalEffectiveBalances = useMultipleContractSingleData(gaugeAddresses, gaugeInterface, 'working_supply') + const weights = useSingleContractMultipleData( + gaugeController, + 'gauge_relative_weight(address)', + gaugeAddresses.map((a) => [a]) + ) + const futureWeights = useSingleContractMultipleData( + gaugeController, + 'get_gauge_weight', + gaugeAddresses.map((a) => [a]) + ) + const lastClaims = useMultipleContractSingleData(gaugeAddresses, gaugeInterface, 'last_claim') + const isKilled = useMultipleContractSingleData(gaugeAddresses, gaugeInterface, 'is_killed') + + const balance = useMultipleContractSingleData(gaugeAddresses, gaugeInterface, 'balanceOf', [ + connected ? address : undefined, + ]) + const claimableMobi = useMultipleContractSingleData(gaugeAddresses, gaugeInterface, 'claimable_tokens', [ + connected ? address : undefined, + ]) + const effectiveBalances = useMultipleContractSingleData(gaugeAddresses, gaugeInterface, 'working_balances', [ + connected ? address : undefined, + ]) + + const lastVote = useSingleContractMultipleData( + gaugeController, + 'last_user_vote', + gaugeAddresses.map((a) => [connected ? address : a, a]) + ) + // vote_user_slopes + const slopes = useSingleContractMultipleData( + gaugeController, + 'vote_user_slopes', + gaugeAddresses.map((a) => [connected ? address : a, a]) + ) + + // useEffect(() => { + // console.log('gauge update') + // connected && + // dispatch( + // updateGaugesUser({ + // userGaugeState: StablePools[CHAIN].map((displayPool, i) => { + // return displayPool.gauge === null + // ? null + // : { + // balance: JSBI.BigInt(balance[i].result?.[0] ?? '0'), + // claimableMobi: JSBI.BigInt(claimableMobi[i].result?.[0] ?? '0'), + // lastVote: parseInt(lastVote[i].result?.[0].toString() ?? '0'), + // powerAllocated: parseInt(slopes[i].result?.[1] ?? '0'), + // effectiveBalance: JSBI.BigInt(effectiveBalances[i].result?.[0] ?? '0'), + // } + // }), + // }) + // ) + // dispatch( + // updateGauges({ + // gaugeState: StablePools[CHAIN].map((display, i) => { + // return display.gauge === null + // ? null + // : { + // isKilled: isKilled[0].result?.[0] === true, + // lastClaim: parseInt(lastClaims?.[i]?.result?.[0].toString() ?? '0'), + // weight: JSBI.BigInt(weights[i].result?.[0] ?? 0), + // futureWeight: JSBI.BigInt(futureWeights[i].result?.[0] ?? 0), + // totalSupply: JSBI.BigInt(totalSupply[i].result?.[0] ?? 0), + // workingSupply: JSBI.BigInt(workingSupply[i].result?.[0] ?? 0), + // totalEffectiveBalance: JSBI.BigInt(totalEffectiveBalances[i].result?.[0] ?? 0), + // } + // }), + // }) + // ) + // }, [ + // connected, + // dispatch, + // blockNumber, + // balance, + // claimableMobi, + // lastVote, + // slopes, + // isKilled, + // lastClaims, + // totalSupply, + // workingSupply, + // weights, + // futureWeights, + // effectiveBalances, + // totalEffectiveBalances, + // ]) + + return null +} diff --git a/src/state/governance/hooks.ts b/src/state/governance/hooks.ts index 186dd954aa9..912a6dd2ff6 100644 --- a/src/state/governance/hooks.ts +++ b/src/state/governance/hooks.ts @@ -3,11 +3,11 @@ import { Contract } from '@ethersproject/contracts' import { TransactionResponse } from '@ethersproject/providers' import { toUtf8String, Utf8ErrorFuncs } from '@ethersproject/strings' import { formatUnits } from '@ethersproject/units' -// eslint-disable-next-line no-restricted-imports -import { TokenAmount } from '@ubeswap/sdk' import { abi as GOV_ABI } from '@uniswap/governance/build/GovernorAlpha.json' import { useWeb3Context } from 'hooks' import { useGovernanceContract, useVotingEscrowContract } from 'hooks/useContract' +// eslint-disable-next-line no-restricted-imports +import { TokenAmount } from 'lib/token-utils' import { useCallback, useMemo } from 'react' import { calculateGasMargin } from 'utils/calculateGasMargin' @@ -162,6 +162,7 @@ export function useAllProposalData(): { data: ProposalData[]; loading: boolean } // get metadata from past events // early return until events are fetched + // TODO: look at this fix const formattedLogs = useFormattedProposalCreatedLogs(gov, govProposalIndexes) ?? [] return useMemo(() => { const proposalsCallData = proposals diff --git a/src/state/index.ts b/src/state/index.ts index ad265cd1f07..6dd42b82295 100644 --- a/src/state/index.ts +++ b/src/state/index.ts @@ -2,16 +2,15 @@ import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit' import { load, save } from 'redux-localstorage-simple' import application from './application/reducer' -import burn from './burn/reducer' import claim from './claim/reducer' +import gauges from './gauges/reducer' import { updateVersion } from './global/actions' import logs from './logs/slice' import mento from './mento/reducer' import mentoPools from './mentoPools/reducer' -import mint from './mint/reducer' +import pools from './mobiusPools/reducer' import multicall from './multicall/reducer' import openSum from './openSum/reducer' -import stablePools from './stablePools/reducer' import staking from './staking/reducer' import swap from './swap/reducer' import transactions from './transactions/reducer' @@ -25,14 +24,13 @@ const store = configureStore({ user, transactions, swap, - mint, - burn, multicall, - stablePools, + pools, mentoPools, mento, claim, staking, + gauges, logs, openSum, }, diff --git a/src/state/lists/hooks.ts b/src/state/lists/hooks.ts deleted file mode 100644 index 4cff6ae02ff..00000000000 --- a/src/state/lists/hooks.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Token } from '@ubeswap/sdk' -import { Tags, TokenInfo } from '@uniswap/token-lists' - -type TagDetails = Tags[keyof Tags] -export interface TagInfo extends TagDetails { - id: string -} -/** - * Token instances created from token info. - */ -export class WrappedTokenInfo extends Token { - public readonly tokenInfo: TokenInfo - public readonly tags: TagInfo[] - constructor(tokenInfo: TokenInfo, tags: TagInfo[]) { - super(tokenInfo.chainId, tokenInfo.address, tokenInfo.decimals, tokenInfo.symbol, tokenInfo.name) - this.tokenInfo = tokenInfo - this.tags = tags - } - public get logoURI(): string | undefined { - return this.tokenInfo.logoURI - } -} diff --git a/src/state/logs/updater.ts b/src/state/logs/updater.ts index de9baf94513..a4e58faa422 100644 --- a/src/state/logs/updater.ts +++ b/src/state/logs/updater.ts @@ -1,3 +1,4 @@ +import { GOVERNANCE_GENESIS } from 'constants/governance' import { useWeb3Context } from 'hooks' import { useEffect, useMemo } from 'react' @@ -33,13 +34,13 @@ export default function Updater(): null { useEffect(() => { if (!provider || typeof blockNumber !== 'number' || filtersNeedFetch.length === 0) return - + console.log('log update') dispatch(fetchingLogs({ chainId: CHAIN, filters: filtersNeedFetch, blockNumber })) filtersNeedFetch.forEach((filter) => { provider .getLogs({ ...filter, - fromBlock: 0, + fromBlock: GOVERNANCE_GENESIS, toBlock: blockNumber, }) .then((logs) => { diff --git a/src/state/mento/hooks.ts b/src/state/mento/hooks.ts index 21a52c3e39b..6a586a4bbf7 100644 --- a/src/state/mento/hooks.ts +++ b/src/state/mento/hooks.ts @@ -1,15 +1,17 @@ import { parseUnits } from '@ethersproject/units' -import { JSBI, Percent, Price, Token, TokenAmount, TradeType } from '@ubeswap/sdk' +import { TradeType } from '@ubeswap/sdk' import { IMentoExchangeInfo } from 'constants/mento' import { CELO } from 'constants/tokens' -import { useCallback } from 'react' -import { useDispatch, useSelector } from 'react-redux' -import invariant from 'tiny-invariant' +import JSBI from 'jsbi' import { calculateEstimatedSwapInputAmount, calculateEstimatedSwapOutputAmount, calculateSwapPrice, -} from 'utils/mentoCalculator' +} from 'lib/mentoCalculator' +import { Percent, Price, Token, TokenAmount } from 'lib/token-utils' +import { useCallback } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import invariant from 'tiny-invariant' import { BIPS_BASE, CHAIN } from '../../constants' import { useWeb3Context } from '../../hooks' @@ -170,8 +172,8 @@ export function useMentoTradeInfo(): { const parsedAmount = tryParseAmount(typedValue, (isExactIn ? inputCurrency : outputCurrency) ?? undefined) const currencyBalances = { - [Field.INPUT]: relevantTokenBalances[0], - [Field.OUTPUT]: relevantTokenBalances[1], + [Field.INPUT]: relevantTokenBalances[inputCurrency?.address.toLowerCase() ?? ''], + [Field.OUTPUT]: relevantTokenBalances[outputCurrency?.address.toLowerCase() ?? ''], } const currencies: { [field in Field]?: Token } = { diff --git a/src/state/mento/reducer.test.ts b/src/state/mento/reducer.test.ts deleted file mode 100644 index 53ea44fa3e7..00000000000 --- a/src/state/mento/reducer.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { createStore, Store } from 'redux' - -import { Field, selectCurrency } from './actions' -import reducer, { SwapState } from './reducer' - -describe('swap reducer', () => { - let store: Store - - beforeEach(() => { - store = createStore(reducer, { - [Field.OUTPUT]: { currencyId: '' }, - [Field.INPUT]: { currencyId: '' }, - typedValue: '', - independentField: Field.INPUT, - recipient: null, - }) - }) - - describe('selectToken', () => { - it('changes token', () => { - store.dispatch( - selectCurrency({ - field: Field.OUTPUT, - currencyId: '0x0000', - }) - ) - - expect(store.getState()).toEqual({ - [Field.OUTPUT]: { currencyId: '0x0000' }, - [Field.INPUT]: { currencyId: '' }, - typedValue: '', - independentField: Field.INPUT, - recipient: null, - }) - }) - }) -}) diff --git a/src/state/mentoPools/actions.ts b/src/state/mentoPools/actions.ts index 2e53938c10e..06be82b0d1f 100644 --- a/src/state/mentoPools/actions.ts +++ b/src/state/mentoPools/actions.ts @@ -1,4 +1,4 @@ import { createAction } from '@reduxjs/toolkit' import { IMentoExchange, IMentoExchangeInfo } from 'constants/mento' -export const updateMento = createAction<{ mento: IMentoExchangeInfo & IMentoExchange }>('mentoPools/initPools') +export const updateMento = createAction<{ mento: IMentoExchangeInfo & IMentoExchange }>('mentoPools/update') diff --git a/src/state/mentoPools/hooks.ts b/src/state/mentoPools/hooks.ts index 07886019771..6d5643e59d0 100644 --- a/src/state/mentoPools/hooks.ts +++ b/src/state/mentoPools/hooks.ts @@ -1,8 +1,8 @@ import { invariant } from '@apollo/client/utilities/globals' import { StableToken } from '@celo/contractkit' -import { Token } from '@ubeswap/sdk' import { IMentoExchangeInfo } from 'constants/mento' import { CEUR, CREAL, CUSD } from 'constants/tokens' +import { Token } from 'lib/token-utils' import { useSelector } from 'react-redux' import { CHAIN } from '../../constants' diff --git a/src/state/mentoPools/reducer.ts b/src/state/mentoPools/reducer.ts index 07a62b0a67a..b3e5bf378a4 100644 --- a/src/state/mentoPools/reducer.ts +++ b/src/state/mentoPools/reducer.ts @@ -1,7 +1,7 @@ import { createReducer } from '@reduxjs/toolkit' -import { Percent, TokenAmount } from '@ubeswap/sdk' import { IMentoExchange, IMentoExchangeInfo } from 'constants/mento' import { CELO } from 'constants/tokens' +import { Percent, TokenAmount } from 'lib/token-utils' import { CHAIN } from '../../constants' import { MENTO_POOL_INFO } from '../../constants/mento' diff --git a/src/state/mentoPools/updater.ts b/src/state/mentoPools/updater.ts index bcebc212934..c20f2c7593b 100644 --- a/src/state/mentoPools/updater.ts +++ b/src/state/mentoPools/updater.ts @@ -1,53 +1,47 @@ -import { JSBI, Percent, TokenAmount } from '@ubeswap/sdk' -import { CELO } from 'constants/tokens' -import { Exchange } from 'generated' -import { useEffect } from 'react' import { useDispatch } from 'react-redux' import { useBlockNumber } from 'state/application/hooks' -import invariant from 'tiny-invariant' -import { CHAIN, weiScale } from '../../constants' -import { IMentoExchange, MENTO_POOL_INFO } from '../../constants/mento' +import { CHAIN } from '../../constants' +import { MENTO_POOL_INFO } from '../../constants/mento' import { useWeb3Context } from '../../hooks' import { useMentoContract } from '../../hooks/useContract' import { AppDispatch } from '../index' -import { updateMento } from './actions' -import { stableToToken } from './hooks' export function UpdateMento() { - const { provider, kit } = useWeb3Context() + const { kit } = useWeb3Context() const blockNumber = useBlockNumber() const dispatch = useDispatch() const mentoPools = MENTO_POOL_INFO[CHAIN] const mentoContract = useMentoContract('0x12364a15F52b822F12dd858FAeEdC49F472fbA57') - useEffect(() => { - const updatePool = async (poolInfo: IMentoExchange, contract: Exchange | null) => { - if (!contract) return - try { - const balances = (await contract.getBuyAndSellBuckets(false)).map((x) => JSBI.BigInt(x)) - invariant(balances.length === 2, 'mento balances') - const swapFee = JSBI.BigInt(await contract.spread()) - dispatch( - updateMento({ - mento: { - ...poolInfo, - fee: new Percent(swapFee, JSBI.multiply(weiScale, JSBI.BigInt('10000'))), - address: contract.address, - celoReserve: new TokenAmount(CELO[CHAIN], balances[0]), - stableReserve: new TokenAmount(stableToToken(poolInfo.stable), balances[1]), - }, - }) - ) - } catch (error) { - console.error(error) - } - } - mentoPools.forEach(async (pool) => { - const address = await kit.registry.addressFor(pool.contract) - updatePool(pool, mentoContract?.attach(address) ?? null) - }) - }, [blockNumber, provider, dispatch, kit.contracts, mentoContract, kit.registry, mentoPools]) + // useEffect(() => { + // const updatePool = async (poolInfo: IMentoExchange, contract: Exchange | null) => { + // if (!contract) return + // try { + // const balances = (await contract.getBuyAndSellBuckets(false)).map((x) => JSBI.BigInt(x)) + // invariant(balances.length === 2, 'mento balances') + // const swapFee = JSBI.BigInt(await contract.spread()) + // console.log('mento update') + // dispatch( + // updateMento({ + // mento: { + // ...poolInfo, + // fee: new Percent(swapFee, JSBI.multiply(weiScale, JSBI.BigInt('10000'))), + // address: contract.address, + // celoReserve: new TokenAmount(CELO[CHAIN], balances[0]), + // stableReserve: new TokenAmount(stableToToken(poolInfo.stable), balances[1]), + // }, + // }) + // ) + // } catch (error) { + // console.error(error) + // } + // } + // mentoPools.forEach(async (pool) => { + // const address = await kit.registry.addressFor(pool.contract) + // updatePool(pool, mentoContract?.attach(address) ?? null) + // }) + // }, [blockNumber, dispatch, kit.contracts, mentoContract, kit.registry, mentoPools]) return null } diff --git a/src/state/mint/actions.ts b/src/state/mint/actions.ts deleted file mode 100644 index 4fcd912c514..00000000000 --- a/src/state/mint/actions.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { createAction } from '@reduxjs/toolkit' - -export enum Field { - CURRENCY_A = 'CURRENCY_A', - CURRENCY_B = 'CURRENCY_B', -} - -export const typeInput = createAction<{ field: Field; typedValue: string; noLiquidity: boolean }>('mint/typeInputMint') -export const resetMintState = createAction('mint/resetMintState') diff --git a/src/state/mint/reducer.test.ts b/src/state/mint/reducer.test.ts deleted file mode 100644 index 92abd422779..00000000000 --- a/src/state/mint/reducer.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { createStore, Store } from 'redux' - -import { Field, typeInput } from './actions' -import reducer, { MintState } from './reducer' - -describe('mint reducer', () => { - let store: Store - - beforeEach(() => { - store = createStore(reducer, { - independentField: Field.CURRENCY_A, - typedValue: '', - otherTypedValue: '', - }) - }) - - describe('typeInput', () => { - it('sets typed value', () => { - store.dispatch(typeInput({ field: Field.CURRENCY_A, typedValue: '1.0', noLiquidity: false })) - expect(store.getState()).toEqual({ independentField: Field.CURRENCY_A, typedValue: '1.0', otherTypedValue: '' }) - }) - it('clears other value', () => { - store.dispatch(typeInput({ field: Field.CURRENCY_A, typedValue: '1.0', noLiquidity: false })) - store.dispatch(typeInput({ field: Field.CURRENCY_B, typedValue: '1.0', noLiquidity: false })) - expect(store.getState()).toEqual({ independentField: Field.CURRENCY_B, typedValue: '1.0', otherTypedValue: '' }) - }) - }) -}) diff --git a/src/state/mint/reducer.ts b/src/state/mint/reducer.ts deleted file mode 100644 index 6de567875f8..00000000000 --- a/src/state/mint/reducer.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { createReducer } from '@reduxjs/toolkit' - -import { Field, resetMintState, typeInput } from './actions' - -export interface MintState { - readonly independentField: Field - readonly typedValue: string - readonly otherTypedValue: string // for the case when there's no liquidity -} - -const initialState: MintState = { - independentField: Field.CURRENCY_A, - typedValue: '', - otherTypedValue: '', -} - -export default createReducer(initialState, (builder) => - builder - .addCase(resetMintState, () => initialState) - .addCase(typeInput, (state, { payload: { field, typedValue, noLiquidity } }) => { - if (noLiquidity) { - // they're typing into the field they've last typed in - if (field === state.independentField) { - return { - ...state, - independentField: field, - typedValue, - } - } - // they're typing into a new field, store the other value - else { - return { - ...state, - independentField: field, - typedValue, - otherTypedValue: state.typedValue, - } - } - } else { - return { - ...state, - independentField: field, - typedValue, - otherTypedValue: '', - } - } - }) -) diff --git a/src/state/mobiusPools/actions.ts b/src/state/mobiusPools/actions.ts new file mode 100644 index 00000000000..2117f26f051 --- /dev/null +++ b/src/state/mobiusPools/actions.ts @@ -0,0 +1,4 @@ +import { createAction } from '@reduxjs/toolkit' +import { IExchange, IExchangeInfo, Volume } from 'constants/pools' + +export const updatePools = createAction<{ pools: (IExchangeInfo & IExchange & Volume)[] }>('pools/updatePools') diff --git a/src/state/mobiusPools/hooks.ts b/src/state/mobiusPools/hooks.ts new file mode 100644 index 00000000000..030c537c078 --- /dev/null +++ b/src/state/mobiusPools/hooks.ts @@ -0,0 +1,108 @@ +import { DisplayPool, IExchange, IExchangeInfo, StablePools, Volume } from 'constants/pools' +import { useWeb3Context } from 'hooks' +import { TokenAmount } from 'lib/token-utils' +import { useSelector } from 'react-redux' +import { useTokenBalance, useTokenBalances } from 'state/wallet/hooks' +import invariant from 'tiny-invariant' + +import { CHAIN } from '../../constants' +import { AppState } from '..' + +export function useCurrentPool(token0: string, token1: string): IExchangeInfo | null { + const pools = useSelector((state) => + state.pools.pools.filter((pool) => { + const tokens = pool.tokens.map((t) => t.address) + return tokens.includes(token0) && tokens.includes(token1) + }) + ) + if (pools.length === 0) return null + invariant(pools.length === 1) + return pools[0] +} + +export function useCurrentPoolAddress(exchangeAddress: string): IExchangeInfo | null { + const pools = useSelector((state) => + state.pools.pools.filter((pool) => pool.address === exchangeAddress) + ) + if (pools.length === 0) return null + invariant(pools.length === 1) + return pools[0] +} + +export function getCurrentExchangeAddress(exchangeAddress: string): IExchange | null { + const stablePools = StablePools[CHAIN] + const pools = stablePools.filter(({ pool }) => pool.address === exchangeAddress) + if (pools.length === 0) return null + invariant(pools.length === 1) + return pools[0].pool +} + +export function getCurrentDisplayAddress(exchangeAddress: string): DisplayPool | null { + const stablePools = StablePools[CHAIN] + const pools = stablePools.filter(({ pool }) => pool.address === exchangeAddress) + if (pools.length === 0) return null + invariant(pools.length === 1) + return pools[0] +} + +export function usePools(): readonly IExchangeInfo[] { + return useSelector((state) => state.pools.pools) +} + +export function useCurrentPoolVolume(exchangeAddress: string): Volume | null { + const pools = useSelector((state) => + state.pools.pools.filter((pool) => pool.address === exchangeAddress) + ) + if (pools.length === 0) return null + invariant(pools.length === 1) + return pools[0] +} + +export function usePoolsVolume(): readonly Volume[] { + return useSelector((state) => state.pools.pools.map((p) => p as Volume)) +} + +export function poolInfoToExchange(info: IExchangeInfo): IExchange { + const exchange = StablePools[CHAIN].filter( + (e) => e.pool.lpToken.address.toLowerCase() === info.lpTotalSupply.token.address.toLowerCase() + ) + invariant(exchange.length === 1, 'cant find exchange') + return exchange[0].pool +} + +export function poolInfoToDisplay(info: IExchangeInfo): DisplayPool { + const exchange = StablePools[CHAIN].filter( + (e) => e.pool.lpToken.address.toLowerCase() === info.lpTotalSupply.token.address.toLowerCase() + ) + invariant(exchange.length === 1, 'cant find exchange') + return exchange[0] +} + +export function getCurrentDisplayFromGauge(gaugeAddress: string): DisplayPool | null { + const stablePools = StablePools[CHAIN] + const pools = stablePools.filter((pool) => pool.gauge?.address === gaugeAddress) + if (pools.length === 0) return null + invariant(pools.length === 1) + return pools[0] +} + +export function useLpBalance(exchange: IExchange): TokenAmount { + const { address, connected } = useWeb3Context() + const balance = useTokenBalance(address, exchange.lpToken) + return connected ? balance ?? new TokenAmount(exchange.lpToken, 0) : new TokenAmount(exchange.lpToken, 0) +} + +export function useAllLpBalances(): TokenAmount[] { + const { address, connected } = useWeb3Context() + + const balances = useTokenBalances( + connected ? address : undefined, + StablePools[CHAIN].map(({ pool }) => pool.lpToken) + ) + + return StablePools[CHAIN].map((el) => + connected + ? balances[el.pool.lpToken.address] ?? new TokenAmount(el.pool.lpToken, 0) + : new TokenAmount(el.pool.lpToken, 0) + ) +} diff --git a/src/state/mobiusPools/reducer.ts b/src/state/mobiusPools/reducer.ts new file mode 100644 index 00000000000..de23e7a8c51 --- /dev/null +++ b/src/state/mobiusPools/reducer.ts @@ -0,0 +1,34 @@ +import { createReducer } from '@reduxjs/toolkit' +import { IExchange, IExchangeInfo, RECOMMENDED_AMP, RECOMMENDED_FEES, StablePools, Volume } from 'constants/pools' +import { TokenAmount } from 'lib/token-utils' + +import { CHAIN } from '../../constants' +import { updatePools } from './actions' + +export interface Pools { + readonly pools: (IExchangeInfo & IExchange & Volume)[] +} + +function emptyExchangeInfo(exchange: IExchange): IExchangeInfo & IExchange & Volume { + return { + ...exchange, + fees: RECOMMENDED_FEES, + ampFactor: RECOMMENDED_AMP, + lpTotalSupply: new TokenAmount(exchange.lpToken, '0'), + reserves: [new TokenAmount(exchange.tokens[0], '0'), new TokenAmount(exchange.tokens[1], '0')], + volume: null, + } +} + +const initialState: Pools = { + pools: StablePools[CHAIN].map((p) => emptyExchangeInfo(p.pool)), +} + +export default createReducer(initialState, (builder) => + builder.addCase(updatePools, (state, { payload: { pools } }) => { + return { + ...state, + pools: pools, + } + }) +) diff --git a/src/state/mobiusPools/updater.ts b/src/state/mobiusPools/updater.ts new file mode 100644 index 00000000000..e1a186bdb67 --- /dev/null +++ b/src/state/mobiusPools/updater.ts @@ -0,0 +1,98 @@ +import { gql, useQuery } from '@apollo/client' +import { Interface } from '@ethersproject/abi' +import { StablePools } from 'constants/pools' +import { useDispatch } from 'react-redux' +import { useBlockNumber } from 'state/application/hooks' +import { useMultipleContractSingleData } from 'state/multicall/hooks' + +import { CHAIN } from '../../constants' +import LP from '../../constants/abis/LPToken.json' +import SWAP from '../../constants/abis/Swap.json' +import { AppDispatch } from '../index' + +const lpInterface = new Interface(LP.abi) +const SwapInterface = new Interface(SWAP.abi) + +function index(swaps: any[]): { [address: string]: any } { + return swaps.reduce((acc: { [address: string]: any }, cur: any) => { + return { ...acc, [cur.id.toLowerCase()]: cur } + }, {}) + + // pegQueries.reduce((acc, cur) => { + // return { ...acc, [cur]: (resp.data[cur]?.['usd'] as number).toString() } + // }, {}) +} + +export function UpdatePools() { + const blockNumber = useBlockNumber() + const dispatch = useDispatch() + const stablePools = StablePools[CHAIN] + + const lpTokenAddress = stablePools.map((p) => p.pool.lpToken.address) + const poolAddress = stablePools.map((p) => p.pool.address) + + const lpTotalSupply = useMultipleContractSingleData(lpTokenAddress, lpInterface, 'totalSupply') + const balances = useMultipleContractSingleData(poolAddress, SwapInterface, 'getBalances') + + const query = gql` + { + swaps { + id + A + balances + virtualPrice + dailyVolumes(orderBy: timestamp, orderDirection: desc) { + volume + } + weeklyVolumes(first: 2, orderBy: timestamp, orderDirection: desc) { + volume + } + } + } + ` + const { data, loading, error } = useQuery(query) + // useEffect(() => { + // // const inSubgraph: Set = + // // data?.swaps.reduce((accum: Set, cur: any) => new Set([...accum, cur.id]), new Set()) ?? new Set() + // try { + // if (error) console.log(error) + // const volume = loading ? null : index(data.swaps) + // console.log('pools update') + // dispatch( + // updatePools({ + // pools: stablePools.map((displayPool, i) => { + // return { + // ...displayPool.pool, + // fees: RECOMMENDED_FEES, + // volume: + // volume && volume[displayPool.pool.address] + // ? { + // total: volume[displayPool.pool.address].dailyVolumes.reduce( + // (accum: number, el: any) => accum + parseFloat(el.volume), + // 0 + // ), + // day: parseFloat(volume[displayPool.pool.address].dailyVolumes[0]?.volume ?? '0'), + // week: parseFloat(volume[displayPool.pool.address].weeklyVolumes[0]?.volume ?? '0'), + // } + // : null, + // ampFactor: RECOMMENDED_AMP, + // lpTotalSupply: new TokenAmount( + // displayPool.pool.lpToken, + // JSBI.BigInt(lpTotalSupply[i]?.result?.[0] ?? '0') + // ), + // reserves: balances?.[i]?.result?.[0] + // ? balances?.[i]?.result?.[0].map( + // (amt: BigInt, j: number): TokenAmount => + // new TokenAmount(displayPool.pool.tokens[j], BigIntToJSBI(amt)) + // ) + // : [new TokenAmount(displayPool.pool.tokens[0], '0'), new TokenAmount(displayPool.pool.tokens[1], '0')], + // } + // }), + // }) + // ) + // } catch (error) { + // console.error(error) + // } + // }, [dispatch, stablePools, lpTotalSupply, blockNumber, balances, error, loading, data]) + return null +} diff --git a/src/state/multicall/actions.test.ts b/src/state/multicall/actions.test.ts deleted file mode 100644 index 8d2b816cbbb..00000000000 --- a/src/state/multicall/actions.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { parseCallKey, toCallKey } from './actions' - -describe('actions', () => { - describe('#parseCallKey', () => { - it('does not throw for invalid address', () => { - expect(parseCallKey('0x-0x')).toEqual({ address: '0x', callData: '0x' }) - }) - it('does not throw for invalid calldata', () => { - expect(parseCallKey('0x6b175474e89094c44da98b954eedeac495271d0f-abc')).toEqual({ - address: '0x6b175474e89094c44da98b954eedeac495271d0f', - callData: 'abc', - }) - }) - it('throws for invalid format', () => { - expect(() => parseCallKey('abc')).toThrow('Invalid call key: abc') - }) - it('throws for uppercase calldata', () => { - expect(parseCallKey('0x6b175474e89094c44da98b954eedeac495271d0f-0xabcD')).toEqual({ - address: '0x6b175474e89094c44da98b954eedeac495271d0f', - callData: '0xabcD', - }) - }) - it('parses pieces into address', () => { - expect(parseCallKey('0x6b175474e89094c44da98b954eedeac495271d0f-0xabcd')).toEqual({ - address: '0x6b175474e89094c44da98b954eedeac495271d0f', - callData: '0xabcd', - }) - }) - }) - - describe('#toCallKey', () => { - it('throws for invalid address', () => { - expect(() => toCallKey({ callData: '0x', address: '0x' })).toThrow('Invalid address: 0x') - }) - it('throws for invalid calldata', () => { - expect(() => - toCallKey({ - address: '0x6b175474e89094c44da98b954eedeac495271d0f', - callData: 'abc', - }) - ).toThrow('Invalid hex: abc') - }) - it('throws for uppercase hex', () => { - expect(() => - toCallKey({ - address: '0x6b175474e89094c44da98b954eedeac495271d0f', - callData: '0xabcD', - }) - ).toThrow('Invalid hex: 0xabcD') - }) - it('concatenates address to data', () => { - expect(toCallKey({ address: '0x6b175474e89094c44da98b954eedeac495271d0f', callData: '0xabcd' })).toEqual( - '0x6b175474e89094c44da98b954eedeac495271d0f-0xabcd' - ) - }) - }) -}) diff --git a/src/state/multicall/hooks.ts b/src/state/multicall/hooks.ts index 174ce372688..2767061cd26 100644 --- a/src/state/multicall/hooks.ts +++ b/src/state/multicall/hooks.ts @@ -51,6 +51,7 @@ export const NEVER_RELOAD: ListenerOptions = { // the lowest level call for subscribing to contract data function useCallsData(calls: (Call | undefined)[], options?: ListenerOptions): CallResult[] { + const chainId = CHAIN const callResults = useSelector( (state) => state.multicall.callResults ) @@ -70,11 +71,11 @@ function useCallsData(calls: (Call | undefined)[], options?: ListenerOptions): C // update listeners when there is an actual change that persists for at least 100ms useEffect(() => { const callKeys: string[] = JSON.parse(serializedCallKeys) - if (callKeys.length === 0) return undefined + if (!chainId || callKeys.length === 0) return undefined const calls = callKeys.map((key) => parseCallKey(key)) dispatch( addMulticallListeners({ - chainId: CHAIN, + chainId, calls, options, }) @@ -83,20 +84,20 @@ function useCallsData(calls: (Call | undefined)[], options?: ListenerOptions): C return () => { dispatch( removeMulticallListeners({ - chainId: CHAIN, + chainId, calls, options, }) ) } - }, [dispatch, options, serializedCallKeys]) + }, [chainId, dispatch, options, serializedCallKeys]) return useMemo( () => calls.map((call) => { - if (!call) return INVALID_RESULT + if (!chainId || !call) return INVALID_RESULT - const result = callResults[CHAIN]?.[toCallKey(call)] + const result = callResults[chainId]?.[toCallKey(call)] let data if (result?.data && result?.data !== '0x') { data = result.data @@ -104,7 +105,7 @@ function useCallsData(calls: (Call | undefined)[], options?: ListenerOptions): C return { valid: true, data, blockNumber: result?.blockNumber } }), - [callResults, calls] + [callResults, calls, chainId] ) } @@ -252,33 +253,6 @@ export function useSingleCallResult( const result = useCallsData(calls, options)[0] const latestBlockNumber = useBlockNumber() - // if (methodName === 'calculateTokenAmount') { - // console.log({ - // calls, - // fragment, - // result, - // inputs, - // }) - // } - // useEffect(() => { - // console.log({ calls }) - // }, [calls]) - // useEffect(() => { - // console.log({ fragment }) - // }, [fragment]) - // useEffect(() => { - // console.log({ result }) - // }, [result]) - // useEffect(() => { - // console.log({ inputs }) - // }, [inputs]) - // useEffect(() => { - // console.log({ methodName }) - // }, [methodName]) - // useEffect(() => { - // console.log({ latestBlockNumber }) - // }, [latestBlockNumber]) - return useMemo(() => { return toCallState(result, contract?.interface, fragment, latestBlockNumber) }, [result, contract, fragment, latestBlockNumber]) diff --git a/src/state/multicall/reducer.test.ts b/src/state/multicall/reducer.test.ts deleted file mode 100644 index eaffd5e6a2d..00000000000 --- a/src/state/multicall/reducer.test.ts +++ /dev/null @@ -1,312 +0,0 @@ -import { createStore, Store } from '@reduxjs/toolkit' - -import { - addMulticallListeners, - errorFetchingMulticallResults, - fetchingMulticallResults, - removeMulticallListeners, - updateMulticallResults, -} from './actions' -import reducer, { MulticallState } from './reducer' - -const DAI_ADDRESS = '0x6b175474e89094c44da98b954eedeac495271d0f' - -describe('multicall reducer', () => { - let store: Store - beforeEach(() => { - store = createStore(reducer) - }) - - it('has correct initial state', () => { - expect(store.getState().callResults).toEqual({}) - expect(store.getState().callListeners).toEqual(undefined) - }) - - describe('addMulticallListeners', () => { - it('adds listeners', () => { - store.dispatch( - addMulticallListeners({ - chainId: 1, - calls: [ - { - address: DAI_ADDRESS, - callData: '0x', - }, - ], - }) - ) - expect(store.getState()).toEqual({ - callListeners: { - [1]: { - [`${DAI_ADDRESS}-0x`]: { - [1]: 1, - }, - }, - }, - callResults: {}, - }) - }) - }) - - describe('removeMulticallListeners', () => { - it('noop', () => { - store.dispatch( - removeMulticallListeners({ - calls: [ - { - address: DAI_ADDRESS, - callData: '0x', - }, - ], - chainId: 1, - }) - ) - expect(store.getState()).toEqual({ callResults: {}, callListeners: {} }) - }) - it('removes listeners', () => { - store.dispatch( - addMulticallListeners({ - chainId: 1, - calls: [ - { - address: DAI_ADDRESS, - callData: '0x', - }, - ], - }) - ) - store.dispatch( - removeMulticallListeners({ - calls: [ - { - address: DAI_ADDRESS, - callData: '0x', - }, - ], - chainId: 1, - }) - ) - expect(store.getState()).toEqual({ - callResults: {}, - callListeners: { [1]: { [`${DAI_ADDRESS}-0x`]: {} } }, - }) - }) - }) - - describe('updateMulticallResults', () => { - it('updates data if not present', () => { - store.dispatch( - updateMulticallResults({ - chainId: 1, - blockNumber: 1, - results: { - abc: '0x', - }, - }) - ) - expect(store.getState()).toEqual({ - callResults: { - [1]: { - abc: { - blockNumber: 1, - data: '0x', - }, - }, - }, - }) - }) - it('updates old data', () => { - store.dispatch( - updateMulticallResults({ - chainId: 1, - blockNumber: 1, - results: { - abc: '0x', - }, - }) - ) - store.dispatch( - updateMulticallResults({ - chainId: 1, - blockNumber: 2, - results: { - abc: '0x2', - }, - }) - ) - expect(store.getState()).toEqual({ - callResults: { - [1]: { - abc: { - blockNumber: 2, - data: '0x2', - }, - }, - }, - }) - }) - it('ignores late updates', () => { - store.dispatch( - updateMulticallResults({ - chainId: 1, - blockNumber: 2, - results: { - abc: '0x2', - }, - }) - ) - store.dispatch( - updateMulticallResults({ - chainId: 1, - blockNumber: 1, - results: { - abc: '0x1', - }, - }) - ) - expect(store.getState()).toEqual({ - callResults: { - [1]: { - abc: { - blockNumber: 2, - data: '0x2', - }, - }, - }, - }) - }) - }) - describe('fetchingMulticallResults', () => { - it('updates state to fetching', () => { - store.dispatch( - fetchingMulticallResults({ - chainId: 1, - fetchingBlockNumber: 2, - calls: [{ address: DAI_ADDRESS, callData: '0x0' }], - }) - ) - expect(store.getState()).toEqual({ - callResults: { - [1]: { - [`${DAI_ADDRESS}-0x0`]: { fetchingBlockNumber: 2 }, - }, - }, - }) - }) - - it('updates state to fetching even if already fetching older block', () => { - store.dispatch( - fetchingMulticallResults({ - chainId: 1, - fetchingBlockNumber: 2, - calls: [{ address: DAI_ADDRESS, callData: '0x0' }], - }) - ) - store.dispatch( - fetchingMulticallResults({ - chainId: 1, - fetchingBlockNumber: 3, - calls: [{ address: DAI_ADDRESS, callData: '0x0' }], - }) - ) - expect(store.getState()).toEqual({ - callResults: { - [1]: { - [`${DAI_ADDRESS}-0x0`]: { fetchingBlockNumber: 3 }, - }, - }, - }) - }) - - it('does not do update if fetching newer block', () => { - store.dispatch( - fetchingMulticallResults({ - chainId: 1, - fetchingBlockNumber: 2, - calls: [{ address: DAI_ADDRESS, callData: '0x0' }], - }) - ) - store.dispatch( - fetchingMulticallResults({ - chainId: 1, - fetchingBlockNumber: 1, - calls: [{ address: DAI_ADDRESS, callData: '0x0' }], - }) - ) - expect(store.getState()).toEqual({ - callResults: { - [1]: { - [`${DAI_ADDRESS}-0x0`]: { fetchingBlockNumber: 2 }, - }, - }, - }) - }) - }) - - describe('errorFetchingMulticallResults', () => { - it('does nothing if not fetching', () => { - store.dispatch( - errorFetchingMulticallResults({ - chainId: 1, - fetchingBlockNumber: 1, - calls: [{ address: DAI_ADDRESS, callData: '0x0' }], - }) - ) - expect(store.getState()).toEqual({ - callResults: { - [1]: {}, - }, - }) - }) - it('updates block number if we were fetching', () => { - store.dispatch( - fetchingMulticallResults({ - chainId: 1, - fetchingBlockNumber: 2, - calls: [{ address: DAI_ADDRESS, callData: '0x0' }], - }) - ) - store.dispatch( - errorFetchingMulticallResults({ - chainId: 1, - fetchingBlockNumber: 2, - calls: [{ address: DAI_ADDRESS, callData: '0x0' }], - }) - ) - expect(store.getState()).toEqual({ - callResults: { - [1]: { - [`${DAI_ADDRESS}-0x0`]: { - blockNumber: 2, - // null data indicates error - data: null, - }, - }, - }, - }) - }) - it('does nothing if not errored on latest block', () => { - store.dispatch( - fetchingMulticallResults({ - chainId: 1, - fetchingBlockNumber: 3, - calls: [{ address: DAI_ADDRESS, callData: '0x0' }], - }) - ) - store.dispatch( - errorFetchingMulticallResults({ - chainId: 1, - fetchingBlockNumber: 2, - calls: [{ address: DAI_ADDRESS, callData: '0x0' }], - }) - ) - expect(store.getState()).toEqual({ - callResults: { - [1]: { - [`${DAI_ADDRESS}-0x0`]: { fetchingBlockNumber: 3 }, - }, - }, - }) - }) - }) -}) diff --git a/src/state/multicall/updater.test.ts b/src/state/multicall/updater.test.ts deleted file mode 100644 index feaad4f36a2..00000000000 --- a/src/state/multicall/updater.test.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { activeListeningKeys, outdatedListeningKeys } from './updater' - -describe('multicall updater', () => { - describe('#activeListeningKeys', () => { - it('ignores 0, returns call key to block age key', () => { - expect( - activeListeningKeys( - { - [1]: { - ['abc']: { - 4: 2, // 2 listeners care about 4 block old data - 1: 0, // 0 listeners care about 1 block old data - }, - }, - }, - 1 - ) - ).toEqual({ - abc: 4, - }) - }) - it('applies min', () => { - expect( - activeListeningKeys( - { - [1]: { - ['abc']: { - 4: 2, // 2 listeners care about 4 block old data - 3: 1, // 1 listener cares about 3 block old data - 1: 0, // 0 listeners care about 1 block old data - }, - }, - }, - 1 - ) - ).toEqual({ - abc: 3, - }) - }) - it('works for infinity', () => { - expect( - activeListeningKeys( - { - [1]: { - ['abc']: { - 4: 2, // 2 listeners care about 4 block old data - 1: 0, // 0 listeners care about 1 block old data - }, - ['def']: { - Infinity: 2, - }, - }, - }, - 1 - ) - ).toEqual({ - abc: 4, - def: Infinity, - }) - }) - it('multiple keys', () => { - expect( - activeListeningKeys( - { - [1]: { - ['abc']: { - 4: 2, // 2 listeners care about 4 block old data - 1: 0, // 0 listeners care about 1 block old data - }, - ['def']: { - 2: 1, - 5: 2, - }, - }, - }, - 1 - ) - ).toEqual({ - abc: 4, - def: 2, - }) - }) - it('ignores negative numbers', () => { - expect( - activeListeningKeys( - { - [1]: { - ['abc']: { - 4: 2, - 1: -1, - [-3]: 4, - }, - }, - }, - 1 - ) - ).toEqual({ - abc: 4, - }) - }) - it('applies min to infinity', () => { - expect( - activeListeningKeys( - { - [1]: { - ['abc']: { - Infinity: 2, // 2 listeners care about any data - 4: 2, // 2 listeners care about 4 block old data - 1: 0, // 0 listeners care about 1 block old data - }, - }, - }, - 1 - ) - ).toEqual({ - abc: 4, - }) - }) - }) - - describe('#outdatedListeningKeys', () => { - it('returns empty if missing block number or chain id', () => { - expect(outdatedListeningKeys({}, { abc: 2 }, undefined, undefined)).toEqual([]) - expect(outdatedListeningKeys({}, { abc: 2 }, 1, undefined)).toEqual([]) - expect(outdatedListeningKeys({}, { abc: 2 }, undefined, 1)).toEqual([]) - }) - it('returns everything for no results', () => { - expect(outdatedListeningKeys({}, { abc: 2, def: 3 }, 1, 1)).toEqual(['abc', 'def']) - }) - it('returns only outdated keys', () => { - expect(outdatedListeningKeys({ [1]: { abc: { data: '0x', blockNumber: 2 } } }, { abc: 1, def: 1 }, 1, 2)).toEqual( - ['def'] - ) - }) - it('returns only keys not being fetched', () => { - expect( - outdatedListeningKeys( - { - [1]: { abc: { data: '0x', blockNumber: 2 }, def: { fetchingBlockNumber: 2 } }, - }, - { abc: 1, def: 1 }, - 1, - 2 - ) - ).toEqual([]) - }) - it('returns keys being fetched for old blocks', () => { - expect( - outdatedListeningKeys( - { [1]: { abc: { data: '0x', blockNumber: 2 }, def: { fetchingBlockNumber: 1 } } }, - { abc: 1, def: 1 }, - 1, - 2 - ) - ).toEqual(['def']) - }) - it('respects blocks per fetch', () => { - expect( - outdatedListeningKeys( - { [1]: { abc: { data: '0x', blockNumber: 2 }, def: { data: '0x', fetchingBlockNumber: 1 } } }, - { abc: 2, def: 2 }, - 1, - 3 - ) - ).toEqual(['def']) - }) - }) -}) diff --git a/src/state/multicall/updater.tsx b/src/state/multicall/updater.tsx index 7acc3d9b0e1..6d893ae912b 100644 --- a/src/state/multicall/updater.tsx +++ b/src/state/multicall/updater.tsx @@ -6,10 +6,16 @@ import { CHAIN } from '../../constants' import { useMulticallContract } from '../../hooks/useContract' import useDebounce from '../../hooks/useDebounce' import chunkArray from '../../utils/chunkArray' -import { CancelledError, retry } from '../../utils/retry' +import { CancelledError, retry, RetryableError } from '../../utils/retry' import { useBlockNumber } from '../application/hooks' import { AppDispatch, AppState } from '../index' -import { Call, fetchingMulticallResults, parseCallKey, updateMulticallResults } from './actions' +import { + Call, + errorFetchingMulticallResults, + fetchingMulticallResults, + parseCallKey, + updateMulticallResults, +} from './actions' // chunk calls so we do not exceed the gas limit const CALL_CHUNK_SIZE = 500 @@ -33,13 +39,13 @@ async function fetchChunk( ) } catch (error) { console.debug('Failed to fetch chunk inside retry', error) - // throw error + throw error } - // if (resultsBlockNumber.toNumber() < minBlockNumber) { - // console.debug(`Fetched results for old block number: ${resultsBlockNumber.toString()} vs. ${minBlockNumber}`) - // throw new RetryableError('Fetched for old block number') - // } - return { results: returnData, blockNumber: resultsBlockNumber } + if (resultsBlockNumber.toNumber() < minBlockNumber) { + console.debug(`Fetched results for old block number: ${resultsBlockNumber.toString()} vs. ${minBlockNumber}`) + throw new RetryableError('Fetched for old block number') + } + return { results: returnData, blockNumber: resultsBlockNumber.toNumber() } } /** @@ -108,28 +114,31 @@ export function outdatedListeningKeys( } export default function Updater(): null { + console.log('multicall updater') const dispatch = useDispatch() const state = useSelector((state) => state.multicall) // wait for listeners to settle before triggering updates const debouncedListeners = useDebounce(state.callListeners, 100) const latestBlockNumber = useBlockNumber() + const chainId = CHAIN const multicallContract = useMulticallContract() const cancellations = useRef<{ blockNumber: number; cancellations: (() => void)[] }>() const listeningKeys: { [callKey: string]: number } = useMemo(() => { - return activeListeningKeys(debouncedListeners, CHAIN) - }, [debouncedListeners]) + return activeListeningKeys(debouncedListeners, chainId) + }, [debouncedListeners, chainId]) const unserializedOutdatedCallKeys = useMemo(() => { - return outdatedListeningKeys(state.callResults, listeningKeys, CHAIN, latestBlockNumber) - }, [state.callResults, listeningKeys, latestBlockNumber]) + return outdatedListeningKeys(state.callResults, listeningKeys, chainId, latestBlockNumber) + }, [chainId, state.callResults, listeningKeys, latestBlockNumber]) const serializedOutdatedCallKeys = useMemo( () => JSON.stringify(unserializedOutdatedCallKeys.sort()), [unserializedOutdatedCallKeys] ) + useEffect(() => { - if (!latestBlockNumber || !multicallContract) return + if (!latestBlockNumber || !chainId || !multicallContract) return const outdatedCallKeys: string[] = JSON.parse(serializedOutdatedCallKeys) if (outdatedCallKeys.length === 0) return @@ -137,14 +146,14 @@ export default function Updater(): null { const chunkedCalls = chunkArray(calls, CALL_CHUNK_SIZE) - if (cancellations.current?.blockNumber !== latestBlockNumber) { + if (cancellations.current && latestBlockNumber - cancellations.current?.blockNumber > 2) { cancellations.current?.cancellations?.forEach((c) => c()) } dispatch( fetchingMulticallResults({ calls, - chainId: CHAIN, + chainId, fetchingBlockNumber: latestBlockNumber, }) ) @@ -154,7 +163,7 @@ export default function Updater(): null { cancellations: chunkedCalls.map((chunk, index) => { const { cancel, promise } = retry(() => fetchChunk(multicallContract, chunk, latestBlockNumber), { n: Infinity, - minWait: 2500, + minWait: 1500, maxWait: 3500, }) promise @@ -164,10 +173,10 @@ export default function Updater(): null { // accumulates the length of all previous indices const firstCallKeyIndex = chunkedCalls.slice(0, index).reduce((memo, curr) => memo + curr.length, 0) const lastCallKeyIndex = firstCallKeyIndex + returnData.length - + console.log(3) dispatch( updateMulticallResults({ - chainId: CHAIN, + chainId, results: outdatedCallKeys .slice(firstCallKeyIndex, lastCallKeyIndex) .reduce<{ [callKey: string]: string | null }>((memo, callKey, i) => { @@ -183,19 +192,19 @@ export default function Updater(): null { console.debug('Cancelled fetch for blockNumber', latestBlockNumber) return } - console.error('Failed to fetch multicall chunk', chunk, CHAIN, error) - // dispatch( - // errorFetchingMulticallResults({ - // calls: chunk, - // chainId, - // fetchingBlockNumber: latestBlockNumber, - // }) - // ) + console.error('Failed to fetch multicall chunk', chunk, chainId, error) + dispatch( + errorFetchingMulticallResults({ + calls: chunk, + chainId, + fetchingBlockNumber: latestBlockNumber, + }) + ) }) return cancel }), } - }, [dispatch, latestBlockNumber, multicallContract, serializedOutdatedCallKeys]) + }, [chainId, multicallContract, dispatch, serializedOutdatedCallKeys, latestBlockNumber]) return null } diff --git a/src/state/openSum/actions.ts b/src/state/openSum/actions.ts index 436d653a350..515b176ef4d 100644 --- a/src/state/openSum/actions.ts +++ b/src/state/openSum/actions.ts @@ -1,4 +1,4 @@ import { createAction } from '@reduxjs/toolkit' -import { JSBI } from '@ubeswap/sdk' +import JSBI from 'jsbi' export const updateBalances = createAction<{ balances: JSBI[][] }>('openSum/updateBalances') diff --git a/src/state/openSum/hooks.ts b/src/state/openSum/hooks.ts index ae109b1487e..411d6fce1c2 100644 --- a/src/state/openSum/hooks.ts +++ b/src/state/openSum/hooks.ts @@ -1,6 +1,7 @@ // To-Do: Implement Hooks to update Client-Side contract representation -import { ChainId, JSBI, Price, Token, TokenAmount } from '@ubeswap/sdk' import { ConstantSum, ConstantSumInfo } from 'constants/ConstantSum' +import JSBI from 'jsbi' +import { ChainId, Price, Token, TokenAmount, ZERO } from 'lib/token-utils' import { useSelector } from 'react-redux' import { tryParseAmount } from 'state/swap/hooks' @@ -8,7 +9,6 @@ import { CHAIN } from '../../constants' import { AppState } from '..' import { ConstantSumPool } from './reducer' -const ZERO = JSBI.BigInt('0') export interface MentoPoolInfo { readonly poolAddress?: string readonly tokens: readonly Token[] diff --git a/src/state/openSum/reducer.ts b/src/state/openSum/reducer.ts index fc1cdcfdcc6..155e4998fce 100644 --- a/src/state/openSum/reducer.ts +++ b/src/state/openSum/reducer.ts @@ -1,14 +1,14 @@ import { createReducer } from '@reduxjs/toolkit' -import { JSBI } from '@ubeswap/sdk' -import { NETWORK_CHAIN_ID } from 'connectors' import { ConstantSum } from 'constants/ConstantSum' -import { WrappedTokenInfo } from 'state/lists/hooks' +import JSBI from 'jsbi' +import { Token } from 'lib/token-utils' +import { CHAIN } from '../../constants' import { updateBalances } from './actions' export type ConstantSumPool = { address: string - tokens: [WrappedTokenInfo, WrappedTokenInfo] + tokens: [Token, Token] balances?: JSBI[] } @@ -17,7 +17,7 @@ export interface PoolState { } const initialState: PoolState = { - pools: ConstantSum[NETWORK_CHAIN_ID] ?? [], + pools: ConstantSum[CHAIN] ?? [], } export default createReducer(initialState, (builder) => diff --git a/src/state/openSum/updater.ts b/src/state/openSum/updater.ts index cd0bf334aae..5a91afb6662 100644 --- a/src/state/openSum/updater.ts +++ b/src/state/openSum/updater.ts @@ -1,15 +1,16 @@ import { Interface } from '@ethersproject/abi' -import { JSBI } from '@ubeswap/sdk' import { ConstantSum, ConstantSumInfo } from 'constants/ConstantSum' -import { useMemo } from 'react' +import JSBI from 'jsbi' import { useDispatch } from 'react-redux' import { useMultipleContractSingleData } from 'state/multicall/hooks' import { CHAIN } from '../../constants' import CONSTANT_SUM from '../../constants/abis/ConstantSum.json' import { AppDispatch } from '../index' -import { BigIntToJSBI } from '../stablePools/updater' -import { updateBalances } from './actions' + +export const BigIntToJSBI = (num: BigInt | undefined, fallBack = '0') => { + return JSBI.BigInt(num?.toString() ?? fallBack) +} const ConstantSumInterface = new Interface(CONSTANT_SUM.abi) const ZERO = JSBI.BigInt('0') @@ -23,13 +24,13 @@ export function UpdateOpenSum(): null { ConstantSumInterface, 'getBalances' ) - - useMemo(() => { - const balances = balancesMany?.map( - ({ result }) => result?.[0].map((n) => BigIntToJSBI(n as BigInt, '0')) ?? [ZERO, ZERO] - ) - dispatch(updateBalances({ balances })) - }, [dispatch, balancesMany]) + console.log('migrate update') + // useMemo(() => { + // const balances = balancesMany?.map( + // ({ result }) => result?.[0].map((n) => BigIntToJSBI(n as BigInt, '0')) ?? [ZERO, ZERO] + // ) + // dispatch(updateBalances({ balances })) + // }, [dispatch, balancesMany]) return null } diff --git a/src/state/stablePools/actions.ts b/src/state/stablePools/actions.ts deleted file mode 100644 index e5cf344d741..00000000000 --- a/src/state/stablePools/actions.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { createAction } from '@reduxjs/toolkit' - -import { ExternalRewards, GaugeOnlyInfo, PoolOnlyInfo, StableSwapConstants, StableSwapVariable } from './reducer' - -export const initPools = createAction<{ pools: StableSwapConstants }>('stablePools/initPools') - -export const updateVariableData = createAction<{ address: string; variableData: StableSwapVariable }>( - 'stablePools/updateVariableData' -) - -export const updateExternalRewards = createAction<{ pool: string; externalRewards: ExternalRewards[] }>( - 'stablePools/updateExternalRewards' -) - -export const updatePools = createAction<{ info: PoolOnlyInfo[] }>('stablePools/updatePools') - -export const updateGauges = createAction<{ info: GaugeOnlyInfo[] }>('stablePools/updateGauges') diff --git a/src/state/stablePools/hooks.ts b/src/state/stablePools/hooks.ts deleted file mode 100644 index 9ad1e2f043b..00000000000 --- a/src/state/stablePools/hooks.ts +++ /dev/null @@ -1,271 +0,0 @@ -// To-Do: Implement Hooks to update Client-Side contract representation -import { JSBI, Percent, Token, TokenAmount } from '@ubeswap/sdk' -import { Chain, Coins } from 'constants/StablePools' -import { useWeb3Context } from 'hooks' -import { addressToToken } from 'hooks/Tokens' -import { useLiquidityGaugeContract, useStableSwapContract } from 'hooks/useContract' -import { useEffect, useMemo, useState } from 'react' -import { useSelector } from 'react-redux' -import { useEthBtcPrice } from 'state/application/hooks' -import { useSingleContractMultipleData } from 'state/multicall/hooks' -import { tryParseAmount } from 'state/swap/hooks' -import invariant from 'tiny-invariant' -import { PairStableSwap } from 'utils/StablePairMath' - -import WARNINGS from '../../constants/PoolWarnings.json' -import { StableSwapMath } from '../../utils/stableSwapMath' -import { AppState } from '..' -import { StableSwapConstants, StableSwapPool, WarningType } from './reducer' -import { BigIntToJSBI } from './updater' - -export type WarningModifications = 'require-equal-deposit' | 'none' - -export interface StablePoolInfo { - readonly name: string - readonly poolAddress?: string - readonly stakingToken?: Token - readonly lpToken?: Token - readonly tokens: readonly Token[] - readonly amountDeposited?: TokenAmount - readonly totalDeposited: TokenAmount - readonly apr?: TokenAmount - readonly totalStakedAmount?: TokenAmount - readonly workingSupply?: JSBI - readonly stakedAmount: TokenAmount - readonly totalVolume?: TokenAmount - readonly peggedTo: string - readonly displayDecimals: number - readonly virtualPrice: JSBI - readonly priceOfStaked: TokenAmount - readonly balances: TokenAmount[] - readonly pegComesAfter: boolean | undefined - readonly mobiRate: JSBI | undefined - readonly pendingMobi: JSBI | undefined - readonly gaugeAddress?: string - readonly workingPercentage: Percent - readonly totalPercentage: Percent - readonly externalRewardRates?: TokenAmount[] - readonly lastClaim?: Date - readonly meta?: string - readonly displayChain: Chain - readonly coin: Coins - readonly isDisabled?: boolean - readonly weeklyVolume?: TokenAmount - readonly poolLoading: boolean - readonly gaugeLoading: boolean - readonly isKilled?: boolean -} - -export function useCurrentPool(tok1: string, tok2: string): readonly [StableSwapPool | undefined] { - const withMetaPools = useSelector((state) => - Object.values(state.stablePools.pools).map(({ pool }) => { - if (!pool.metaPool || pool.disabled) return pool - const underlying = state.stablePools.pools[pool.metaPool]?.pool - return { - ...pool, - tokenAddresses: pool.tokenAddresses.concat(underlying.tokenAddresses), - } - }) - ) - const pools = withMetaPools.filter(({ tokenAddresses }) => { - return tokenAddresses.includes(tok1) && tokenAddresses.includes(tok2) - }) - - return [pools.length > 0 ? pools[0] : null] -} - -export function usePools(): readonly StableSwapPool[] { - const pools = useSelector((state) => - Object.values(state.stablePools.pools).map(({ pool }) => pool) - ) - return pools -} - -const tokenAmountScaled = (token: Token, amount: JSBI): TokenAmount => - new TokenAmount(token, JSBI.divide(amount, JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt(token.decimals)))) - -export const getPoolInfo = (pool: StableSwapPool): StablePoolInfo | Record | undefined => { - const external = - pool.additionalRewardRate?.map((rate, i) => { - const token = addressToToken(pool.additionalRewards?.[i]) - invariant(token) - return new TokenAmount(token, rate) - }) ?? undefined - return !pool.lpTotalSupply - ? undefined - : { - name: pool.name, - poolAddress: pool.address, - lpToken: pool.lpToken, - tokens: pool.tokens, - amountDeposited: new TokenAmount( - pool.lpToken, - JSBI.add(pool.lpOwned ?? JSBI.BigInt('0'), pool.userStaked ?? JSBI.BigInt('0')) - ), - totalDeposited: new TokenAmount(pool.lpToken, pool.lpTotalSupply ?? JSBI.BigInt('0')), - stakedAmount: new TokenAmount(pool.lpToken, pool.userStaked || JSBI.BigInt('0')), - apr: new TokenAmount(pool.lpToken, JSBI.BigInt('100000000000000000')), - peggedTo: pool.peggedTo, - virtualPrice: pool.virtualPrice, - priceOfStaked: tokenAmountScaled( - pool.lpToken, - JSBI.multiply( - pool.virtualPrice ?? JSBI.BigInt('0'), - JSBI.add(pool.lpOwned ?? JSBI.BigInt('0'), pool.userStaked ?? JSBI.BigInt('0')) - ) - ), - workingSupply: pool.workingLiquidity, - balances: pool.tokens.map( - (token, i) => new TokenAmount(token, pool.balances?.[i] ?? pool.approxBalances?.[i] ?? '0') - ), - pegComesAfter: pool.pegComesAfter, - mobiRate: pool.isKilled ? JSBI.BigInt('0') : pool.totalMobiRate, - pendingMobi: pool.pendingMobi, - gaugeAddress: pool.gaugeAddress, - displayDecimals: pool.displayDecimals, - totalStakedAmount: new TokenAmount(pool.lpToken, pool.totalStakedAmount ?? '0'), - workingPercentage: new Percent(pool.effectiveBalance, pool.totalEffectiveBalance), - totalPercentage: new Percent(pool.userStaked ?? '0', pool.totalStakedAmount ?? '1'), - externalRewardRates: external, - lastClaim: pool.lastClaim, - meta: pool.metaPool, - displayChain: pool.displayChain, - coin: pool.coin, - isDisabled: pool.disabled, - isKilled: pool.isKilled, - weeklyVolume: pool.volume ? tryParseAmount(pool.volume.week.toFixed(6), pool.lpToken) : undefined, - totalVolume: pool.volume ? tryParseAmount(pool.volume.total?.toFixed(6), pool.lpToken) : undefined, - poolLoading: pool.loadingPool, - gaugeLoading: pool.loadingGauge, - } -} - -export function useStablePoolInfoByName(name: string): StablePoolInfo | undefined { - const pool = useSelector((state) => state.stablePools.pools[name.toLowerCase()]?.pool) - return !pool ? undefined : { ...getPoolInfo(pool) } -} - -export function useStablePoolInfo(): readonly StablePoolInfo[] { - const pools = usePools() - return pools.map((pool) => getPoolInfo(pool)).filter((el) => el) -} - -export function useExpectedTokens(pool: StablePoolInfo, lpAmount: TokenAmount): TokenAmount[] { - const contract = useStableSwapContract(pool.poolAddress) - const { tokens } = pool - const { address } = useWeb3Context() - const [expectedOut, setExpectedOut] = useState( - tokens.map((token) => new TokenAmount(token, JSBI.BigInt('0'))) - ) - useEffect(() => { - const updateData = async () => { - try { - const newTokenAmounts = await contract?.calculateRemoveLiquidity(address, lpAmount.raw.toString()) - setExpectedOut(tokens.map((token, i) => new TokenAmount(token, JSBI.BigInt(newTokenAmounts[i].toString())))) - } catch (e) { - console.error(e) - setExpectedOut(tokens.map((token, i) => new TokenAmount(token, JSBI.BigInt('0')))) - } - } - lpAmount && lpAmount.raw && updateData() - }, [address, contract, lpAmount, tokens]) - return expectedOut -} - -export function useExpectedLpTokens( - pool: StablePoolInfo, - tokens: Token[], - input: (string | undefined)[], - isDeposit = true -): [TokenAmount, TokenAmount[]] { - const mathUtil = useMathUtil(pool.poolAddress ?? pool.address) - const tokenAmounts = useMemo( - () => tokens.map((t, i) => tryParseAmount(input[i], t) ?? new TokenAmount(t, '0')), - [input] - ) - - return useMemo(() => { - const allZero = tokenAmounts.reduce((accum, cur) => accum && cur.equalTo('0'), true) - if (allZero) { - return [new TokenAmount(pool.lpToken, '0'), tokenAmounts] - } - - if (!pool.totalDeposited || pool.totalDeposited.equalTo('0')) { - const amount = - tryParseAmount( - tokenAmounts.reduce((accum, cur) => (parseFloat(accum) + parseFloat(cur.toExact())).toString(), '0'), - pool.lpToken - ) ?? new TokenAmount(pool.lpToken, '0') - - return [amount, tokenAmounts] - } - const amount = - mathUtil?.calculateTokenAmount( - tokenAmounts.map((ta) => ta?.raw || JSBI.BigInt('0')), - isDeposit - ) ?? JSBI.BigInt('0') - return [new TokenAmount(pool.lpToken, amount), tokenAmounts] - }, [input, mathUtil, tokenAmounts]) -} - -export function useMathUtil(pool: StableSwapPool | string): StableSwapMath | undefined { - const name = !pool ? '' : typeof pool == 'string' ? pool : pool.address - const math = useSelector((state) => state.stablePools.pools[name.toLowerCase()]?.math) - return math -} - -export function usePool(): readonly [StableSwapPool] { - const [tok1, tok2] = useSelector((state) => [ - state.swap.INPUT.currencyId, - state.swap.OUTPUT.currencyId, - ]) - return useCurrentPool(tok1, tok2) -} - -export function usePriceOfLp(address: string, amountOfLp: TokenAmount): TokenAmount | undefined { - const pool = useStablePoolInfoByName(address) - const price = useEthBtcPrice(pool?.poolAddress ?? '') - return pool && price && amountOfLp - ? new TokenAmount( - amountOfLp.token, - JSBI.divide( - JSBI.multiply(amountOfLp.raw, JSBI.multiply(pool?.virtualPrice, price)), - JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('18')) - ) - ) - : undefined -} - -export function useExternalRewards({ address }: { address: string }): TokenAmount[] { - const pool = useSelector((state) => state.stablePools.pools[address.toLowerCase()]?.pool) - const gauge = useLiquidityGaugeContract(pool?.gaugeAddress ?? undefined) - const { address: userAddress, connected } = useWeb3Context() - gauge?.claimable_reward_write - const claimableTokens = useSingleContractMultipleData( - gauge, - 'claimable_reward_write', - pool?.additionalRewards?.map((token) => [connected ? userAddress : undefined, token ?? undefined]) ?? undefined - ) - // console.log(claimableTokens) - const externalRewards = claimableTokens?.map((result, i) => { - const token = addressToToken(pool.additionalRewards?.[i]) - invariant(token) - return new TokenAmount(token, BigIntToJSBI(result?.result?.[0] ?? '0', '0') ?? '0') - }) - return externalRewards -} - -export function useWarning( - pool: string | undefined -): { warning: string; link?: string; modification?: WarningModifications } | undefined { - const warningType = useSelector( - (state) => state.stablePools.pools[pool?.toLowerCase() ?? '']?.pool?.warningType ?? undefined - ) - if (!warningType) return undefined - return WARNINGS[warningType] as any as { warning: string; link?: string; modification?: WarningModifications } -} - -export function usePairUtil(pool?: StableSwapPool | string): PairStableSwap | undefined { - const name = !pool ? '' : typeof pool == 'string' ? pool : pool.address - return useSelector((state) => state.stablePools.pools[name.toLowerCase()]?.pair) -} diff --git a/src/state/stablePools/reducer.ts b/src/state/stablePools/reducer.ts deleted file mode 100644 index 0bfcf6922ba..00000000000 --- a/src/state/stablePools/reducer.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { createReducer, current } from '@reduxjs/toolkit' -import { Fraction, Percent, Token } from '@ubeswap/sdk' -import { NETWORK_CHAIN_ID } from 'connectors' -import { Chain, Coins, STATIC_POOL_INFO } from 'constants/StablePools' -import JSBI from 'jsbi' -import { PairStableSwap } from 'utils/StablePairMath' -import { StableSwapMath } from 'utils/stableSwapMath' - -import { updateExternalRewards, updateGauges, updatePools } from './actions' - -const ZERO = JSBI.BigInt('0') -export enum WarningType { - POOF = 'poof', -} - -export type ExternalRewards = { - token: string - unclaimed: JSBI -} - -export type PoolOnlyInfo = { - id: string - volume: { - total: number - day: number - week: number - } - approxBalances: JSBI[] - balances: JSBI[] - amp: JSBI - virtualPrice: JSBI - aPrecise: JSBI - lpTotalSupply: JSBI - lpOwned: JSBI - loadingPool: boolean -} - -export type GaugeOnlyInfo = { - id: string - userStaked: JSBI - totalStakedAmount: JSBI - totalMobiRate: JSBI - pendingMobi: JSBI - workingLiquidity: JSBI - poolWeight: Percent - effectiveBalance: JSBI - totalEffectiveBalance: JSBI - lastUserVote: number - powerAllocated: number - futureWeight: JSBI - externalRewards?: ExternalRewards[] - gaugeAddress?: string - relativeGaugeWeight?: Fraction - lastClaim: Date -} - -export type StableSwapVariable = PoolOnlyInfo & GaugeOnlyInfo - -export type StableSwapMathConstants = { - name: string - rates: JSBI[] - lendingPrecision: JSBI - precision: JSBI - feeDenominator: JSBI - precisionMul: JSBI[] - feeIndex: number - decimals: JSBI[] - swapFee: JSBI -} - -export type StableSwapConstants = StableSwapMathConstants & { - tokens: Token[] - tokenAddresses: string[] - address: string - gaugeAddress: string - lpToken: Token - peggedTo: string - pegComesAfter: boolean | undefined - displayDecimals: number - additionalRewards?: string[] - additionalRewardRate?: string[] - lastClaim?: Date - displayChain: Chain - coin: Coins - disabled?: boolean - metaPool?: string - isKilled?: boolean - warningType?: WarningType -} - -export type StableSwapPool = StableSwapConstants & StableSwapVariable -export interface PoolState { - readonly pools: { - [address: string]: { - rehydrate?: boolean - pool: StableSwapPool | StableSwapConstants - math: StableSwapMath | undefined - pair?: PairStableSwap | undefined - } - } -} - -const initialState: PoolState = { - pools: Object.values(STATIC_POOL_INFO[NETWORK_CHAIN_ID]).reduce( - ( - accum: { - [address: string]: { - pool: StableSwapConstants - math: StableSwapMath | undefined - } - }, - cur: StableSwapConstants - ) => ({ - ...accum, - [cur.address.toLowerCase()]: { - pool: { - ...cur, - rehydrate: true, - balances: Array(cur.tokenAddresses.length).fill(ZERO), - userStaked: ZERO, - totalStakedAmount: ZERO, - totalMobiRate: ZERO, - pendingMobi: ZERO, - workingLiquidity: ZERO, - virtualPrice: ZERO, - poolWeight: new Percent('0', '1'), - effectiveBalance: ZERO, - totalEffectiveBalance: ZERO, - futureWeight: ZERO, - lpTotalSupply: ZERO, - lpOwned: ZERO, - loadingGauge: true, - loadingPool: true, - volume: { - day: 0, - week: 0, - }, - }, - math: undefined, - }, - }), - {} - ), -} - -export default createReducer(initialState, (builder) => - builder - .addCase(updateExternalRewards, (state, { payload: { pool, externalRewards } }) => { - if (!state.pools[pool].math) return - state.pools[pool].pool.externalRewards = externalRewards - }) - .addCase(updatePools, (state, { payload: { info } }) => { - const copiedState = current(state) - info.forEach((pool) => { - const cur = copiedState.pools[pool.id].pool as any as StableSwapPool - const newPool = { ...cur, ...pool } - const { balances, aPrecise, swapFee, tokens } = newPool - // console.log( - // balances?.map((el) => el.toString()) ?? ['0', '0'], - // swapFee?.toString() ?? '0', - // aPrecise?.toString() ?? '0' - // ) - const math = new StableSwapMath(newPool) - const pair = - balances && aPrecise && swapFee - ? new PairStableSwap( - balances?.map((el) => el.toString()) ?? ['0', '0'], - swapFee?.toString() ?? '0', - aPrecise?.toString() ?? '0', - tokens.map((t) => t.decimals) - ) - : undefined - - state.pools[pool.id] = { - pool: newPool, - math, - pair, - } - }) - }) - .addCase(updateGauges, (state, { payload: { info } }) => { - const copiedState = current(state) - info.forEach((gauge) => { - const cur = copiedState.pools[gauge.id].pool as any as StableSwapPool - const newPool = { ...cur, ...gauge, loadingGauge: false } - state.pools[gauge.id] = { - pool: newPool, - math: state.pools[gauge.id].math, - } - }) - }) -) diff --git a/src/state/stablePools/updater.ts b/src/state/stablePools/updater.ts deleted file mode 100644 index 0b70b0e9a3e..00000000000 --- a/src/state/stablePools/updater.ts +++ /dev/null @@ -1,231 +0,0 @@ -import { gql, useQuery } from '@apollo/client' -import { Interface } from '@ethersproject/abi' -import { JSBI, Percent } from '@ubeswap/sdk' -import { useMemo } from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { useBlockNumber } from 'state/application/hooks' -import { - useMultipleContractSingleData, - useSingleCallResult, - useSingleContractMultipleData, -} from 'state/multicall/hooks' - -import { CHAIN } from '../../constants' -import GAUGE_V3 from '../../constants/abis/LiquidityGaugeV3.json' -import LP from '../../constants/abis/LPToken.json' -import SWAP from '../../constants/abis/Swap.json' -import { STATIC_POOL_INFO } from '../../constants/StablePools' -import { useWeb3Context } from '../../hooks' -import { useGaugeControllerContract, useMobiContract } from '../../hooks/useContract' -import { AppDispatch, AppState } from '../index' -import { updateGauges, updatePools } from './actions' -import { GaugeOnlyInfo, PoolOnlyInfo, StableSwapConstants } from './reducer' - -const SwapInterface = new Interface(SWAP.abi) -const lpInterface = new Interface(LP.abi) -const gaugeInterface = new Interface(GAUGE_V3.abi) - -export const BigIntToJSBI = (num: BigInt | undefined, fallBack = '0') => { - return JSBI.BigInt(num?.toString() ?? fallBack) -} - -export function UpdateVariablePoolInfo(): null { - const { connected, address } = useWeb3Context() - const blockNumber = useBlockNumber() - const dispatch = useDispatch() - const pools: StableSwapConstants[] = useSelector((state: AppState) => - Object.values(state.stablePools.pools).map(({ pool }) => pool) - ) - const poolAddresses = pools.map(({ address }) => address) - const lpTokenAddresses = pools.map(({ lpToken: { address } }) => address) - const lpTotalSupplies = useMultipleContractSingleData(lpTokenAddresses, lpInterface, 'totalSupply') - const lpOwned_multiple = useMultipleContractSingleData(lpTokenAddresses, lpInterface, 'balanceOf', [ - connected ? address : undefined, - ]) - const virtualPrices = useMultipleContractSingleData(poolAddresses, SwapInterface, 'getVirtualPrice') - const balances = useMultipleContractSingleData(poolAddresses, SwapInterface, 'getBalances') - const amplificationCoefficients = useMultipleContractSingleData(poolAddresses, SwapInterface, 'getAPrecise') - const query = gql` - { - swaps { - id - A - balances - virtualPrice - dailyVolumes(orderBy: timestamp, orderDirection: desc) { - volume - } - weeklyVolumes(first: 2, orderBy: timestamp, orderDirection: desc) { - volume - } - } - } - ` - const { data, loading, error } = useQuery(query) - const lpInfo: { - [address: string]: { total: JSBI; user: JSBI; virtualPrice: JSBI; balances: JSBI[]; aPrecise: JSBI } - } = lpTotalSupplies - .filter((total, i) => !(total?.loading || lpOwned_multiple[i]?.loading)) - .map((total, i) => [ - BigIntToJSBI((total?.result?.[0] as BigInt) ?? '0'), - BigIntToJSBI((lpOwned_multiple?.[i]?.result?.[0] as BigInt) ?? '0'), - BigIntToJSBI((virtualPrices?.[i]?.result?.[0] as BigInt) ?? '0'), - balances?.[i]?.result?.[0] ? balances?.[i]?.result?.[0].map((amt: BigInt): JSBI => BigIntToJSBI(amt)) : undefined, - poolAddresses[i], - BigIntToJSBI((amplificationCoefficients?.[i]?.result?.[0] as BigInt) ?? '50'), - ]) - .reduce( - (accum, [total, user, virtualPrice, balances, address, aPrecise]) => ({ - ...accum, - [(address as any as string).toLowerCase()]: { total, user, balances, virtualPrice, aPrecise }, - }), - {} - ) - const inSubgraph: Set = - data?.swaps.reduce((accum: Set, cur: any) => new Set([...accum, cur.id]), new Set()) ?? new Set() - const poolsNotInSubgraph = poolAddresses.map((a) => a.toLowerCase()).filter((addr) => !inSubgraph.has(addr)) - return useMemo(() => { - if (error) console.log(error) - if (loading) return null - const poolInfo: PoolOnlyInfo[] = data.swaps - .filter(({ id }) => !!lpInfo[id]) - .map((pool) => ({ - id: pool.id, - volume: { - total: pool.dailyVolumes.reduce((accum: number, el: string) => accum + parseFloat(el.volume), 0), - day: parseFloat(pool.dailyVolumes[0]?.volume ?? '0'), - week: parseFloat(pool.weeklyVolumes[0]?.volume ?? '0'), - }, - approxBalances: pool.balances.map((b: string) => JSBI.BigInt(b)), - balances: lpInfo[pool.id].balances ? lpInfo[pool.id].balances : undefined, - aPrecise: lpInfo[pool.id].aPrecise, - virtualPrice: lpInfo[pool.id].virtualPrice, - lpTotalSupply: lpInfo[pool.id].total, - lpOwned: lpInfo[pool.id].user, - loadingPool: !lpInfo[pool.id].total, - })) - - dispatch( - updatePools({ - info: - poolsNotInSubgraph.length > 0 - ? poolInfo.concat( - poolsNotInSubgraph.map((id) => ({ - id, - volume: { - total: undefined, - day: undefined, - week: undefined, - }, - balances: lpInfo[id]?.total ? lpInfo[id].balances : undefined, - aPrecise: JSBI.BigInt(50 * 100), - virtualPrice: lpInfo[id]?.virtualPrice, - lpTotalSupply: lpInfo[id]?.total ?? JSBI.BigInt('1'), - lpOwned: lpInfo[id]?.user ?? JSBI.BigInt('0'), - loadingPool: !lpInfo[id]?.total, - approxBalances: lpInfo[id]?.total ? lpInfo[id].balances : undefined, - })) - ) - : poolInfo, - }) - ) - return null - }, [data, loading, error, dispatch, blockNumber]) -} - -export function BatchUpdateGauges(): null { - const { address, connected } = useWeb3Context() - const blockNumber = useBlockNumber() - const dispatch = useDispatch() - const pools: StableSwapConstants[] = STATIC_POOL_INFO[CHAIN] ?? [] - const gaugeAddresses = pools.map(({ gaugeAddress }) => gaugeAddress) - const gaugeController = useGaugeControllerContract() - const mobiContract = useMobiContract() - - const totalStakedAmount_multi = useMultipleContractSingleData(gaugeAddresses, gaugeInterface, 'totalSupply') - const lpStaked_multi = useMultipleContractSingleData(gaugeAddresses, gaugeInterface, 'balanceOf', [ - connected ? address : undefined, - ]) - const workingLiquidityMulti = useMultipleContractSingleData(gaugeAddresses, gaugeInterface, 'working_supply') - const pendingMobi_multi = useMultipleContractSingleData(gaugeAddresses, gaugeInterface, 'claimable_tokens', [ - connected ? address : undefined, - ]) - const mobiRate: JSBI = BigIntToJSBI(useSingleCallResult(mobiContract, 'rate')?.result?.[0] ?? '0') - const weights = useSingleContractMultipleData( - gaugeController, - 'gauge_relative_weight(address)', - gaugeAddresses.map((a) => [a ?? undefined]) - ) - const futureWeights = useSingleContractMultipleData( - gaugeController, - 'get_gauge_weight', - gaugeAddresses.map((a) => [a ?? undefined]) - ) - - const lastClaims = useMultipleContractSingleData(gaugeAddresses, gaugeInterface, 'last_claim') - - const effectiveBalances = useMultipleContractSingleData(gaugeAddresses, gaugeInterface, 'working_balances', [ - connected ? address : undefined, - ]) - const totalEffectiveBalances = useMultipleContractSingleData(gaugeAddresses, gaugeInterface, 'working_supply') - const lastUserVotes = useSingleContractMultipleData( - gaugeController, - 'last_user_vote', - gaugeAddresses.map((a) => [connected ? address : a, a]) - ) - // vote_user_slopes - const slopes = useSingleContractMultipleData( - gaugeController, - 'vote_user_slopes', - gaugeAddresses.map((a) => [connected ? address : a, a]) - ) - - useMemo(() => { - dispatch( - updateGauges({ - info: pools - .filter((_, i) => !(totalStakedAmount_multi?.[i]?.loading ?? true)) - .map((poolInfo, i) => { - const effectiveBalance: JSBI = BigIntToJSBI((effectiveBalances?.[i]?.result?.[0] as BigInt) ?? '0') - const totalEffectiveBalance: JSBI = BigIntToJSBI( - (totalEffectiveBalances?.[i]?.result?.[0] as BigInt) ?? '1' - ) - const lpStaked: JSBI = BigIntToJSBI((lpStaked_multi?.[i]?.result?.[0] as BigInt) ?? '0') - const pendingMobi: JSBI = BigIntToJSBI((pendingMobi_multi?.[i]?.result?.[0] as BigInt) ?? '0') - const weight: JSBI = BigIntToJSBI((weights?.[i]?.result?.[0] as BigInt) ?? '0') - const futureWeight: JSBI = BigIntToJSBI((futureWeights?.[i]?.result?.[0] as BigInt) ?? '0') - const totalStakedAmount: JSBI = BigIntToJSBI((totalStakedAmount_multi?.[i]?.result?.[0] as BigInt) ?? '0') - const workingLiquidity: JSBI = BigIntToJSBI((workingLiquidityMulti?.[i]?.result?.[0] as BigInt) ?? '0') - const lastUserVote: number = parseInt((lastUserVotes?.[i]?.result?.[0] ?? BigInt('0')).toString() ?? '0') - const lastClaim: Date = new Date( - parseInt((lastClaims?.[i]?.result?.[0] ?? BigInt('0')).toString() ?? '0') * 1000 - ) - const powerAllocated: number = parseInt((slopes?.[i]?.result?.[1] ?? BigInt('0')).toString() ?? '0') - - const totalMobiRate = JSBI.divide( - poolInfo.disabled ? JSBI.BigInt('0') : JSBI.multiply(mobiRate, weight), - JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('18')) - ) - - const collectedData: GaugeOnlyInfo = { - id: poolInfo.address.toLowerCase(), - poolWeight: new Percent(weight, JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('18'))), - userStaked: lpStaked, - pendingMobi, - totalMobiRate, - totalStakedAmount, - workingLiquidity, - effectiveBalance, - totalEffectiveBalance, - lastUserVote, - futureWeight, - lastClaim, - powerAllocated: powerAllocated / 100, - } - return collectedData - }), - }) - ) - }, [blockNumber, dispatch]) - return null -} diff --git a/src/state/stake/hooks.ts b/src/state/stake/hooks.ts deleted file mode 100644 index 637d67a2468..00000000000 --- a/src/state/stake/hooks.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { JSBI, Token, TokenAmount } from '@ubeswap/sdk' -import { MENTO_POOL_INFO } from 'constants/mento' -import { CELO } from 'constants/tokens' -import { stableToToken } from 'state/mentoPools/hooks' -import { StableSwapConstants } from 'state/stablePools/reducer' -import { dedupeTokens } from 'utils/tokens' - -// Hooks -import { CHAIN } from '../../constants' -import { STATIC_POOL_INFO } from '../../constants/StablePools' -import { useWeb3Context } from '../../hooks' -import { tryParseAmount } from '../swap/hooks' - -function inPool(token: Token, pool: StableSwapConstants): boolean { - return pool.tokens.map((t) => t.address === token.address && t.chainId === token.chainId).includes(true) -} - -export function useTokensTradeable(mento: boolean, tokenIn: Token | null | undefined): Token[] { - if (!tokenIn) return [] - const pools = mento - ? tokenIn === CELO[CHAIN] - ? MENTO_POOL_INFO[CHAIN].map((m) => stableToToken(m.stable)) - : [CELO[CHAIN]] - : STATIC_POOL_INFO[CHAIN].filter((pool) => !pool.disabled && inPool(tokenIn, pool)) - .flatMap(({ tokens }) => tokens) - .filter((token) => token !== tokenIn) - return dedupeTokens(pools) -} - -// based on typed value -export function useDerivedStakeInfo( - typedValue: string, - stakingToken: Token, - userLiquidityUnstaked: TokenAmount | undefined -): { - parsedAmount?: TokenAmount - error?: string -} { - const { connected } = useWeb3Context() - - const parsedInput: TokenAmount | undefined = tryParseAmount(typedValue, stakingToken) - - const parsedAmount = - parsedInput && userLiquidityUnstaked && JSBI.lessThanOrEqual(parsedInput.raw, userLiquidityUnstaked.raw) - ? parsedInput - : undefined - - let error: string | undefined - if (!connected) { - error = 'Connect Wallet' - } - if (!parsedAmount) { - error = error ?? 'Enter an amount' - } - - return { - parsedAmount, - error, - } -} diff --git a/src/state/staking/actions.ts b/src/state/staking/actions.ts index e535b75788d..167f14ce729 100644 --- a/src/state/staking/actions.ts +++ b/src/state/staking/actions.ts @@ -1,6 +1,6 @@ import { createAction } from '@reduxjs/toolkit' -import { StakingState } from './reducer' +import { IStakingState, IUserStakingState } from './reducer' -export const updateStaking = createAction<{ stakingInfo: StakingState }>('staking/update') -export const updateSNX = createAction<{ rewardRate: JSBI; leftToClaim: JSBI }>('staking/updateSNX') +export const updateStaking = createAction<{ stakingState: IStakingState }>('staking/update') +export const updateStakingUser = createAction<{ userStakingState: IUserStakingState }>('staking/updateUser') diff --git a/src/state/staking/hooks.ts b/src/state/staking/hooks.ts index fc720972a55..fab27b5e7cf 100644 --- a/src/state/staking/hooks.ts +++ b/src/state/staking/hooks.ts @@ -1,199 +1,168 @@ -import { JSBI, Percent, Token, TokenAmount } from '@ubeswap/sdk' -import { calcApy } from 'components/earn/StablePoolCard' -import { addressToToken, useMobi, useVeMobi } from 'hooks/Tokens' +import { ExternalRewardsToken } from 'constants/staking' +import { useMobi, useVeMobi } from 'hooks/Tokens' +import JSBI from 'jsbi' +import { Percent, Token, TokenAmount } from 'lib/token-utils' import { useSelector } from 'react-redux' import { AppState } from 'state' -import { useTokenPrice } from 'state/application/hooks' -import { getPoolInfo } from 'state/stablePools/hooks' -import { StableSwapPool } from 'state/stablePools/reducer' -import { getDepositValues } from 'utils/stableSwaps' -import { StakingState } from './reducer' +import { CHAIN } from '../../constants' +import { IStakingState, IUserStakingState } from './reducer' -const SECONDS_IN_YEAR = JSBI.BigInt(365 * 24 * 60 * 60) -const SECONDS_IN_WEEK = JSBI.BigInt(7 * 24 * 60 * 60) +export function useStakingState(): IStakingState { + return useSelector((state) => state.staking) +} -export type GaugeSummary = { - pool: string - poolAddress: string - address: string - baseBalance: TokenAmount - totalStaked: TokenAmount - unclaimedMobi: TokenAmount - firstToken: Token - currentWeight: Percent - workingBalance: TokenAmount - totalWorkingBalance: TokenAmount - workingPercentage: Percent - actualPercentage: Percent - lastVote: Date - futureWeight: Percent - powerAllocated: number +export function useUserStakingState(): IUserStakingState { + return useSelector((state) => state.staking) } -export type MobiStakingInfo = { - votingPower: TokenAmount - totalVotingPower: TokenAmount - mobiLocked?: TokenAmount - lockEnd?: Date - positions?: GaugeSummary[] +export function useStakingStateCombined(): IStakingState & IUserStakingState { + return useSelector((state) => state.staking) } export type FeeInfo = { - toClaim?: TokenAmount - totalFeesThisWeek?: TokenAmount - totalFeesNextWeek?: TokenAmount + toClaim: TokenAmount + totalFeesThisWeek: TokenAmount + totalFeesNextWeek: TokenAmount } -export type SNXRewardInfo = { - snxAddress: string - rewardToken: Token - rewardRate?: TokenAmount - leftToClaim?: TokenAmount - avgApr?: Percent - userRewardRate?: TokenAmount +export function useFeeInfo(): FeeInfo { + const { claimable, total, nextWeek } = useSelector((state: AppState) => ({ + claimable: state.staking.claimableFees, + total: state.staking.feesThisWeek, + nextWeek: state.staking.feesNextWeek, + })) + const mobi = useMobi() + + return { + toClaim: new TokenAmount(mobi, claimable ?? '0'), + totalFeesThisWeek: new TokenAmount(mobi, total ?? '0'), + totalFeesNextWeek: new TokenAmount(mobi, nextWeek ?? '0'), + } } -export function calculateBoostedBalance( - votingPower: JSBI, - totalVotingPower: JSBI, - liquidity: JSBI, - totalLiquidity: JSBI -): JSBI { - let boosted = JSBI.BigInt('0') - if (!JSBI.equal(totalVotingPower, JSBI.BigInt('0'))) - boosted = JSBI.add( - JSBI.divide(JSBI.multiply(JSBI.BigInt(4), liquidity), JSBI.BigInt(10)), - JSBI.divide( - JSBI.multiply(JSBI.BigInt(6), JSBI.multiply(totalLiquidity, votingPower)), - JSBI.multiply(totalVotingPower, JSBI.BigInt(10)) - ) - ) - return JSBI.greaterThan(boosted, liquidity) ? boosted : liquidity + +export type StakingInfo = { + totalWeight: JSBI + totalMobiLocked: TokenAmount + totalVotingPower: TokenAmount + externalRewardRate: TokenAmount + feesThisWeek: JSBI + feesNextWeek: JSBI + mobiRate: TokenAmount } -export function useMobiStakingInfo(): MobiStakingInfo { - const stakingInfo = useSelector((state) => state.staking) - const pools = useSelector((state) => { - const allPools = state.stablePools.pools - return Object.values(allPools).map(({ pool }) => pool) - }) - const veMobi = useVeMobi() +export function useStakingInfo(): StakingInfo { + const stakingState = useStakingState() + const mobi = useMobi() - const baseInfo: MobiStakingInfo = { - votingPower: new TokenAmount(veMobi, stakingInfo.votingPower), - totalVotingPower: new TokenAmount(veMobi, stakingInfo.totalVotingPower), - mobiLocked: new TokenAmount(mobi, stakingInfo.locked?.amount ?? '0'), - lockEnd: stakingInfo.locked ? new Date(stakingInfo.locked.end) : undefined, - } - if (pools && pools.length === 0) { - return baseInfo - } - const positions = pools.map((pool) => ({ - pool: pool.name, - poolAddress: pool.address, - address: pool.gaugeAddress ?? '', - baseBalance: new TokenAmount(pool.lpToken, pool.userStaked ?? '0'), - totalStaked: new TokenAmount(pool.lpToken, pool.totalStakedAmount ?? '0'), - unclaimedMobi: new TokenAmount(mobi, pool.pendingMobi ?? '0'), - firstToken: pool.tokens[0], - currentWeight: pool.poolWeight, - futureWeight: new Percent( - pool.futureWeight, - JSBI.divide(stakingInfo.totalWeight, JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18))) - ), - workingBalance: new TokenAmount(pool.lpToken, pool.effectiveBalance), - totalWorkingBalance: new TokenAmount(pool.lpToken, pool.totalEffectiveBalance), - workingPercentage: new Percent(pool.effectiveBalance, pool.totalEffectiveBalance), - actualPercentage: new Percent(pool.userStaked ?? '0', pool.totalStakedAmount ?? '1'), - lastVote: new Date(pool.lastUserVote * 1000), - powerAllocated: pool.powerAllocated, - })) + const veMobi = useVeMobi() + return { - ...baseInfo, - positions, + totalWeight: stakingState.totalWeight, + totalMobiLocked: new TokenAmount(mobi, stakingState.totalMobiLocked), + totalVotingPower: new TokenAmount(veMobi, stakingState.totalVotingPower), + externalRewardRate: new TokenAmount(ExternalRewardsToken[CHAIN], stakingState.externalRewardsRate), + feesThisWeek: stakingState.feesThisWeek, + feesNextWeek: stakingState.feesNextWeek, + mobiRate: new TokenAmount(mobi, stakingState.mobiRate), } } -export function usePriceOfDeposits() { - const pools = useSelector((state) => { - const allPools = state.stablePools.pools - return Object.values(allPools) - .map(({ pool }) => pool) - .filter((pool) => pool.userStaked && JSBI.greaterThan(pool.userStaked, JSBI.BigInt('0'))) - }) - const prices = useSelector((state: AppState) => ({ - ethPrice: state.application.ethPrice, - btcPrice: state.application.btcPrice, - })) - const dummyToken = useMobi() - return !pools[0] || pools[0].loadingGauge - ? undefined - : new TokenAmount( - dummyToken, - pools.reduce((accum, pool) => { - const address = pool.address - const { valueOfStaked } = getDepositValues(getPoolInfo(pool)) - const price = - address === '0x19260b9b573569dDB105780176547875fE9fedA3' - ? JSBI.BigInt(prices.btcPrice) - : address === '0xE0F2cc70E52f05eDb383313393d88Df2937DA55a' - ? JSBI.BigInt(prices.ethPrice) - : JSBI.BigInt('1') - return JSBI.add(accum, JSBI.multiply(valueOfStaked.raw, price)) - }, JSBI.BigInt('0')) - ) +export type VoteLockInfo = { + locked: TokenAmount + end: number // UNIX time stamp } -export function useLockEnd(): number { - const lockEnd = useSelector((state) => state.staking?.locked?.end ?? 0) - return lockEnd +export type UserStakingInfo = { + voteUserPower: number + votingPower: TokenAmount + claimableExternalRewards: TokenAmount + claimableFees: TokenAmount + lock: VoteLockInfo } -export function useVotePowerLeft(): number { - const votePower = useSelector((state) => state.staking.voteUserPower) - return (10000 - parseInt(votePower.toString())) / 100 -} +export function useUserStakingInfo(): UserStakingInfo { + const userStakingState = useUserStakingState() -export function useSNXRewardInfo(): SNXRewardInfo { - const stakingInfo = useSelector((state) => state.staking) - const snxInfo = stakingInfo.snx const mobi = useMobi() - - const rewardToken = addressToToken(snxInfo?.rewardToken ?? '') - const priceOfReward = useTokenPrice(snxInfo?.rewardToken) - const priceOfMobi = useTokenPrice(mobi?.address) - if (!snxInfo || !snxInfo.tokenRate) return { snxAddress: snxInfo?.address, rewardToken } - const yearlyRate = JSBI.multiply(snxInfo.tokenRate, SECONDS_IN_YEAR) - const apy = - priceOfReward && priceOfMobi - ? calcApy(priceOfReward?.multiply(yearlyRate), priceOfMobi?.multiply(stakingInfo.totalVotingPower))[1] - : undefined - const rewardRate = new TokenAmount(rewardToken, JSBI.multiply(snxInfo.tokenRate, SECONDS_IN_WEEK)) - const userRateJSBI = JSBI.divide(JSBI.multiply(rewardRate.raw, stakingInfo.votingPower), stakingInfo.totalVotingPower) - //rewardRate.multiply(stakingInfo.votingPower).divide(stakingInfo.totalVotingPower) - const userRewardRate = new TokenAmount(rewardToken, userRateJSBI) + const veMobi = useVeMobi() return { - snxAddress: snxInfo.address, - rewardToken, - rewardRate, - leftToClaim: snxInfo.leftToClaim ? new TokenAmount(rewardToken, snxInfo.leftToClaim) : undefined, - avgApr: apy, - userRewardRate, + voteUserPower: parseInt(userStakingState.voteUserPower.toString()), + votingPower: new TokenAmount(veMobi, userStakingState.votingPower), + claimableExternalRewards: new TokenAmount(ExternalRewardsToken[CHAIN], userStakingState.claimableExternalRewards), + claimableFees: new TokenAmount(mobi, userStakingState.claimableFees), + lock: { + locked: new TokenAmount(mobi, userStakingState.lock?.amount), + end: userStakingState.lock.end, + }, } } -export function useFeeInformation(): FeeInfo { - const { claimable, total, nextWeek } = useSelector((state: AppState) => ({ - claimable: state.staking.claimableFees, - total: state.staking.feesThisWeek, - nextWeek: state.staking.feesNextWeek, - })) - const mobi = useMobi() - - return { - toClaim: claimable ? new TokenAmount(mobi, claimable) : undefined, - totalFeesThisWeek: total ? new TokenAmount(mobi, total) : undefined, - totalFeesNextWeek: nextWeek ? new TokenAmount(mobi, nextWeek) : undefined, - } +export type GaugeSummary = { + pool: string + poolAddress: string + address: string + baseBalance: TokenAmount + totalStaked: TokenAmount + unclaimedMobi: TokenAmount + firstToken: Token + currentWeight: Percent + workingBalance: TokenAmount + totalWorkingBalance: TokenAmount + workingPercentage: Percent + actualPercentage: Percent + lastVote: Date + futureWeight: Percent + powerAllocated: number } + +// export function calculateBoostedBalance( +// votingPower: JSBI, +// totalVotingPower: JSBI, +// liquidity: JSBI, +// totalLiquidity: JSBI +// ): JSBI { +// let boosted = JSBI.BigInt('0') +// if (!JSBI.equal(totalVotingPower, JSBI.BigInt('0'))) +// boosted = JSBI.add( +// JSBI.divide(JSBI.multiply(JSBI.BigInt(4), liquidity), JSBI.BigInt(10)), +// JSBI.divide( +// JSBI.multiply(JSBI.BigInt(6), JSBI.multiply(totalLiquidity, votingPower)), +// JSBI.multiply(totalVotingPower, JSBI.BigInt(10)) +// ) +// ) +// return JSBI.greaterThan(boosted, liquidity) ? boosted : liquidity +// } + +// TODO: fix this hook + +// export function usePriceOfDeposits() { +// const pools = useSelector((state) => { +// const allPools = state.stablePools.pools +// return Object.values(allPools) +// .map(({ pool }) => pool) +// .filter((pool) => pool.userStaked && JSBI.greaterThan(pool.userStaked, JSBI.BigInt('0'))) +// }) +// const prices = useSelector((state: AppState) => ({ +// ethPrice: state.application.ethPrice, +// btcPrice: state.application.btcPrice, +// })) +// const dummyToken = useMobi() +// return !pools[0] || pools[0].loadingGauge +// ? undefined +// : new TokenAmount( +// dummyToken, +// pools.reduce((accum, pool) => { +// const address = pool.address +// const { valueOfStaked } = getDepositValues(getPoolInfo(pool)) +// const price = +// address === '0x19260b9b573569dDB105780176547875fE9fedA3' +// ? JSBI.BigInt(prices.btcPrice) +// : address === '0xE0F2cc70E52f05eDb383313393d88Df2937DA55a' +// ? JSBI.BigInt(prices.ethPrice) +// : JSBI.BigInt('1') +// return JSBI.add(accum, JSBI.multiply(valueOfStaked.raw, price)) +// }, JSBI.BigInt('0')) +// ) +// } diff --git a/src/state/staking/reducer.ts b/src/state/staking/reducer.ts index 672485b109d..34571fca245 100644 --- a/src/state/staking/reducer.ts +++ b/src/state/staking/reducer.ts @@ -1,58 +1,62 @@ import { createReducer } from '@reduxjs/toolkit' -import { JSBI } from '@ubeswap/sdk' +import JSBI from 'jsbi' -import { updateSNX, updateStaking } from './actions' +import { updateStaking, updateStakingUser } from './actions' export type VoteLock = { amount: JSBI end: number // UNIX time stamp } -export type SnxRewardsInfo = { - address: string - rewardToken: string - tokenRate?: JSBI - leftToClaim?: JSBI -} - -export type StakingState = { - claimableFees?: JSBI - feesThisWeek?: JSBI - feesNextWeek?: JSBI - votingPower: JSBI - totalVotingPower: JSBI - snx?: SnxRewardsInfo - locked?: VoteLock - voteWeightLeft?: JSBI - voteUserPower: JSBI +export type IStakingState = { totalWeight: JSBI totalMobiLocked: JSBI + totalVotingPower: JSBI + externalRewardsRate: JSBI + feesThisWeek: JSBI + feesNextWeek: JSBI + mobiRate: JSBI } -const SNX_STATIC: SnxRewardsInfo = { - address: '0x0812f6de916667C5aa820E757704c4ac69159529', - rewardToken: '0x471EcE3750Da237f93B8E339c536989b8978a438', +export type IUserStakingState = { + voteUserPower: JSBI + votingPower: JSBI + claimableExternalRewards: JSBI + claimableFees: JSBI + lock: VoteLock } -const initialState: StakingState = { - votingPower: JSBI.BigInt(0), - totalVotingPower: JSBI.BigInt(0), - voteWeightLeft: JSBI.BigInt(0), - voteUserPower: JSBI.BigInt(0), +const initialStakingState: IStakingState = { totalWeight: JSBI.BigInt(0), totalMobiLocked: JSBI.BigInt(1), - snx: SNX_STATIC, + totalVotingPower: JSBI.BigInt(0), + externalRewardsRate: JSBI.BigInt(0), + feesThisWeek: JSBI.BigInt(0), + feesNextWeek: JSBI.BigInt(0), + mobiRate: JSBI.BigInt(0), +} + +const initialUserStakingState: IUserStakingState = { + voteUserPower: JSBI.BigInt(0), + votingPower: JSBI.BigInt(0), + claimableExternalRewards: JSBI.BigInt(0), + claimableFees: JSBI.BigInt(0), + lock: { amount: JSBI.BigInt(0), end: 0 }, +} + +const initialState: IStakingState & IUserStakingState = { + ...initialStakingState, + ...initialUserStakingState, } -export default createReducer(initialState, (builder) => { - builder.addCase(updateStaking, (state, { payload: { stakingInfo } }) => ({ - ...state, - ...stakingInfo, - })) - builder.addCase(updateSNX, (state, { payload }) => { - if (state.snx) { - state.snx.tokenRate = payload.rewardRate ?? state.snx.tokenRate - state.snx.leftToClaim = payload.leftToClaim ?? state.snx.leftToClaim - } - }) +export default createReducer(initialState, (builder) => { + builder + .addCase(updateStaking, (state, { payload: { stakingState } }) => ({ + ...state, + ...stakingState, + })) + .addCase(updateStakingUser, (state, { payload: { userStakingState } }) => ({ + ...state, + ...userStakingState, + })) }) diff --git a/src/state/staking/updater.ts b/src/state/staking/updater.ts index 48f52cf75cb..98bd5f414e8 100644 --- a/src/state/staking/updater.ts +++ b/src/state/staking/updater.ts @@ -1,11 +1,12 @@ -import { JSBI } from '@ubeswap/sdk' +import { ExternalStakingRewards } from 'constants/staking' +import { VEMOBI } from 'constants/tokens' import { useWeb3Context } from 'hooks' -import { roundDate } from 'pages/Staking/Lock' -import { useDispatch, useSelector } from 'react-redux' -import { AppDispatch, AppState } from 'state' +import { roundDate } from 'pages/Staking/Lock/Lock' +import { useDispatch } from 'react-redux' +import { AppDispatch } from 'state' import { useSingleCallResult } from 'state/multicall/hooks' -import { SECONDS_IN_WEEK } from '../../constants' +import { CHAIN, SECONDS_IN_WEEK } from '../../constants' import { useFeeDistributor, useGaugeControllerContract, @@ -13,29 +14,27 @@ import { useStakingContract, useVotingEscrowContract, } from '../../hooks/useContract' -import { updateSNX, updateStaking } from './actions' - -// pools, general staking, general gauges, user staking, user gauges export default function StakingUpdater() { - const snxAddress: string | undefined = useSelector((state: AppState) => state.staking.snx?.address) const dispatch = useDispatch() const mobiContract = useMobiContract() const votingEscrow = useVotingEscrowContract() const controller = useGaugeControllerContract() - const snxContract = useStakingContract(snxAddress) + const snxContract = useStakingContract(ExternalStakingRewards[CHAIN]) const feeDistributorContract = useFeeDistributor() - const { address, connected } = useWeb3Context() const votingPower = useSingleCallResult(votingEscrow, 'balanceOf(address)', [connected ? address : undefined]) - const totalVotingPower = useSingleCallResult(votingEscrow, 'totalSupply()') - const totalMobiLocked = useSingleCallResult(mobiContract, 'balanceOf(address)', [votingEscrow?.address ?? undefined]) const locked = useSingleCallResult(votingEscrow, 'locked', [connected ? address : undefined]) + const snxToClaim = useSingleCallResult(snxContract, 'earned(address)', [connected ? address : undefined]) const allocatedPower = useSingleCallResult(controller, 'vote_user_power', [connected ? address : undefined]) + + const totalVotingPower = useSingleCallResult(votingEscrow, 'totalSupply()') const totalWeight = useSingleCallResult(controller, 'get_total_weight') + const totalMobiLocked = useSingleCallResult(mobiContract, 'balanceOf(address)', [VEMOBI[CHAIN].address]) const snxRewardRate = useSingleCallResult(snxContract, 'rewardRate()') - const snxToClaim = useSingleCallResult(snxContract, 'earned(address)', [connected ? address : undefined]) + const mobiRate = useSingleCallResult(mobiContract, 'rate') + const feesToClaim = useSingleCallResult(feeDistributorContract, 'claim()') const totalFeesNextWeek = useSingleCallResult(feeDistributorContract, 'tokens_per_week', [ (roundDate(Date.now()).valueOf() / 1000).toFixed(0), @@ -43,30 +42,53 @@ export default function StakingUpdater() { const totalFeesThisWeek = useSingleCallResult(feeDistributorContract, 'tokens_per_week', [ (roundDate(Date.now()).valueOf() / 1000 - SECONDS_IN_WEEK).toFixed(0), ]) + // useEffect(() => { + // console.log('staking update') + // connected && + // dispatch( + // updateStakingUser({ + // userStakingState: { + // voteUserPower: JSBI.BigInt(allocatedPower?.result?.[0] ?? '0'), + // votingPower: JSBI.BigInt(votingPower?.result?.[0] ?? '0'), + // claimableExternalRewards: JSBI.BigInt(snxToClaim?.result?.[0] ?? '0'), + // claimableFees: JSBI.BigInt(feesToClaim?.result?.[0] ?? '0'), + // lock: { + // amount: JSBI.BigInt(locked?.result?.amount ?? '0'), + // end: parseInt(locked?.result?.end.toString()) * 1000, // Need unix in milliseconds + // }, + // }, + // }) + // ) + // dispatch( + // updateStaking({ + // stakingState: { + // totalVotingPower: JSBI.BigInt(totalVotingPower?.result?.[0] ?? '0'), + // totalWeight: JSBI.BigInt(totalWeight?.result?.[0] ?? '0'), + // totalMobiLocked: JSBI.BigInt(totalMobiLocked?.result?.[0] ?? '0'), + // externalRewardsRate: JSBI.BigInt(snxRewardRate?.result?.[0] ?? '0'), + // feesThisWeek: JSBI.BigInt(totalFeesThisWeek?.result?.[0] ?? '0'), + // feesNextWeek: JSBI.BigInt(totalFeesNextWeek?.result?.[0] ?? '0'), + // mobiRate: JSBI.BigInt(mobiRate?.result?.[0] ?? '0'), + // }, + // }) + // ) + // }, [ + // allocatedPower?.result, + // connected, + // dispatch, + // feesToClaim?.result, + // locked?.result?.amount, + // locked?.result?.end, + // mobiRate?.result, + // snxRewardRate?.result, + // snxToClaim?.result, + // totalFeesNextWeek?.result, + // totalFeesThisWeek?.result, + // totalMobiLocked?.result, + // totalVotingPower?.result, + // totalWeight?.result, + // votingPower?.result, + // ]) - dispatch( - updateSNX({ - rewardRate: snxRewardRate?.result ? JSBI.BigInt(snxRewardRate?.result?.[0] ?? '0') : undefined, - leftToClaim: snxToClaim?.result ? JSBI.BigInt(snxToClaim?.result?.[0] ?? '0') : undefined, - }) - ) - dispatch( - updateStaking({ - stakingInfo: { - votingPower: JSBI.BigInt(votingPower?.result?.[0] ?? '0'), - totalVotingPower: JSBI.BigInt(totalVotingPower?.result?.[0] ?? '0'), - locked: { - amount: JSBI.BigInt(locked?.result?.amount ?? '0'), - end: parseInt(locked?.result?.end.toString()) * 1000, // Need unix in milliseconds - }, - voteUserPower: JSBI.BigInt(allocatedPower?.result?.[0] ?? '0'), - totalWeight: JSBI.BigInt(totalWeight?.result?.[0] ?? '0'), - totalMobiLocked: JSBI.BigInt(totalMobiLocked?.result?.[0] ?? '0'), - claimableFees: JSBI.BigInt(feesToClaim?.result?.[0] ?? '0'), - feesThisWeek: JSBI.BigInt(totalFeesThisWeek?.result?.[0] ?? '0'), - feesNextWeek: JSBI.BigInt(totalFeesNextWeek?.result?.[0] ?? '0'), - }, - }) - ) return null } diff --git a/src/state/swap/hooks.ts b/src/state/swap/hooks.ts index d49fd34dc8f..b261f73245a 100644 --- a/src/state/swap/hooks.ts +++ b/src/state/swap/hooks.ts @@ -1,16 +1,19 @@ import { parseUnits } from '@ethersproject/units' -import { JSBI, Percent, Price, Token, TokenAmount, TradeType } from '@ubeswap/sdk' +import { TradeType } from '@ubeswap/sdk' +import { IExchangeInfo } from 'constants/pools' +import JSBI from 'jsbi' +import { calculateEstimatedSwapOutputAmount, calculateSwapPrice } from 'lib/calculator' +import { Percent, Price, Token, TokenAmount } from 'lib/token-utils' import { useCallback } from 'react' import { useDispatch, useSelector } from 'react-redux' -import { StableSwapPool } from 'state/stablePools/reducer' +import { useCurrentPool, usePools } from 'state/mobiusPools/hooks' import invariant from 'tiny-invariant' -import { PairStableSwap } from 'utils/StablePairMath' +import { BIPS_BASE } from '../../constants' import { useWeb3Context } from '../../hooks' import { useCurrency } from '../../hooks/Tokens' import { isAddress } from '../../utils' import { AppDispatch, AppState } from '../index' -import { useCurrentPool, usePairUtil, usePools } from '../stablePools/hooks' import { useTokenBalances } from '../wallet/hooks' import { Field, selectCurrency, setRecipient, switchCurrencies, typeInput } from './actions' @@ -84,7 +87,7 @@ export function tryParseAmount(value?: string, currency?: Token): TokenAmount | export type MobiusTrade = { input: TokenAmount output: TokenAmount - pool: StableSwapPool + pool: IExchangeInfo indexFrom: number indexTo: number executionPrice: Price @@ -96,15 +99,12 @@ export type MobiusTrade = { function calcInputOutput( input: Token | undefined, output: Token | undefined, - isExactIn: boolean, parsedAmount: TokenAmount | undefined, - math: PairStableSwap, - poolInfo: StableSwapPool + poolInfo: IExchangeInfo ): readonly [TokenAmount | undefined, TokenAmount | undefined, TokenAmount | undefined] { if ((!input && !output) || !parsedAmount) { return [undefined, undefined, undefined] } - const { tokens } = poolInfo if (!output) { return [parsedAmount, undefined, undefined] } @@ -112,9 +112,6 @@ function calcInputOutput( return [undefined, parsedAmount, undefined] } - const indexFrom = tokens.map(({ address }) => address).indexOf(input.address) - const indexTo = tokens.map(({ address }) => address).indexOf(output.address) - const details: [TokenAmount | undefined, TokenAmount | undefined, TokenAmount | undefined] = [ undefined, undefined, @@ -122,10 +119,12 @@ function calcInputOutput( ] invariant(parsedAmount) + details[0] = parsedAmount - const [expectedOut, fee] = math.outputAmount(indexFrom, indexTo, parsedAmount.raw) - details[1] = new TokenAmount(output, expectedOut) - details[2] = new TokenAmount(input, fee) + const { fee, outputAmount } = calculateEstimatedSwapOutputAmount(poolInfo, parsedAmount) + details[1] = outputAmount + details[2] = fee + return details } @@ -139,7 +138,6 @@ export function useMobiusTradeInfo(): { const { address, connected } = useWeb3Context() const { - independentField, typedValue, [Field.INPUT]: { currencyId: inputCurrencyId }, [Field.OUTPUT]: { currencyId: outputCurrencyId }, @@ -149,8 +147,7 @@ export function useMobiusTradeInfo(): { const pools = usePools() const poolsLoading = pools.length === 0 - const [pool] = useCurrentPool(inputCurrency?.address ?? '', outputCurrency?.address ?? '') - const mathUtil = usePairUtil(pool) + const pool = useCurrentPool(inputCurrency?.address ?? '', outputCurrency?.address ?? '') const to: string | null = connected ? address : null const relevantTokenBalances = useTokenBalances(connected ? address : undefined, [ @@ -158,8 +155,7 @@ export function useMobiusTradeInfo(): { outputCurrency ?? undefined, ]) - const isExactIn: boolean = independentField === Field.INPUT - const parsedAmount = tryParseAmount(typedValue, (isExactIn ? inputCurrency : outputCurrency) ?? undefined) + const parsedAmount = tryParseAmount(typedValue, inputCurrency ?? undefined) const currencyBalances = { [Field.INPUT]: relevantTokenBalances[0], @@ -179,11 +175,11 @@ export function useMobiusTradeInfo(): { if (!parsedAmount) { inputError = inputError ?? 'Enter an amount' } - if (!pool || pool.loadingPool) { + if (!pool) { inputError = inputError ?? 'Pool Info Loading' } - if (pool && JSBI.equal(pool.lpTotalSupply, JSBI.BigInt('0'))) { + if (pool && JSBI.equal(pool.lpTotalSupply.raw, JSBI.BigInt('0'))) { inputError = inputError ?? 'Insufficient Liquidity' } @@ -199,9 +195,8 @@ export function useMobiusTradeInfo(): { !outputCurrency || !parsedAmount || !pool || - !mathUtil || poolsLoading || - JSBI.equal(pool.lpTotalSupply, JSBI.BigInt('0')) + JSBI.equal(pool.lpTotalSupply.raw, JSBI.BigInt('0')) ) { return { currencies, @@ -211,26 +206,19 @@ export function useMobiusTradeInfo(): { v2Trade: undefined, } } - const { tokens = [] } = pool || {} + const tokens = pool.reserves.map(({ token }) => token) const indexFrom = inputCurrency ? tokens.map(({ address }) => address).indexOf(inputCurrency.address) : 0 const indexTo = outputCurrency ? tokens.map(({ address }) => address).indexOf(outputCurrency.address) : 0 - const tradeData = calcInputOutput(inputCurrency, outputCurrency, isExactIn, parsedAmount, mathUtil, pool) + const tradeData = calcInputOutput(inputCurrency, outputCurrency, parsedAmount, pool) - const basisTrade = calcInputOutput( - inputCurrency, - outputCurrency, - isExactIn, - tryParseAmount('1', inputCurrency), - mathUtil, - pool - ) + const basisPrice = indexFrom === 1 ? calculateSwapPrice(pool) : calculateSwapPrice(pool).invert() const input = tradeData[0] const output = tradeData[1] const fee = tradeData[2] - if (!input || !output || !fee || !basisTrade[0] || !basisTrade[1]) { + if (!input || !output || !fee || !basisPrice) { return { currencies, currencyBalances, @@ -245,8 +233,10 @@ export function useMobiusTradeInfo(): { } const executionPrice = new Price(inputCurrency, outputCurrency, input?.raw, output?.raw) - const basisPrice = new Price(inputCurrency, outputCurrency, basisTrade[0]?.raw, basisTrade[1]?.raw) - const priceImpactFraction = basisPrice.subtract(executionPrice).divide(basisPrice) + const priceImpactFraction = basisPrice + .subtract(executionPrice) + .divide(basisPrice) + .subtract(pool.fees.trade.divide(BIPS_BASE).multiply('100')) const priceImpact = new Percent(priceImpactFraction.numerator, priceImpactFraction.denominator) const v2Trade: MobiusTrade | undefined = diff --git a/src/state/swap/reducer.test.ts b/src/state/swap/reducer.test.ts deleted file mode 100644 index 53ea44fa3e7..00000000000 --- a/src/state/swap/reducer.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { createStore, Store } from 'redux' - -import { Field, selectCurrency } from './actions' -import reducer, { SwapState } from './reducer' - -describe('swap reducer', () => { - let store: Store - - beforeEach(() => { - store = createStore(reducer, { - [Field.OUTPUT]: { currencyId: '' }, - [Field.INPUT]: { currencyId: '' }, - typedValue: '', - independentField: Field.INPUT, - recipient: null, - }) - }) - - describe('selectToken', () => { - it('changes token', () => { - store.dispatch( - selectCurrency({ - field: Field.OUTPUT, - currencyId: '0x0000', - }) - ) - - expect(store.getState()).toEqual({ - [Field.OUTPUT]: { currencyId: '0x0000' }, - [Field.INPUT]: { currencyId: '' }, - typedValue: '', - independentField: Field.INPUT, - recipient: null, - }) - }) - }) -}) diff --git a/src/state/swap/reducer.ts b/src/state/swap/reducer.ts index 6e277361ee7..82250c23360 100644 --- a/src/state/swap/reducer.ts +++ b/src/state/swap/reducer.ts @@ -44,9 +44,6 @@ export default createReducer(initialState, (builder) => if (!!state[Field.INPUT].currencyId && !!state[Field.OUTPUT].currencyId) { return { ...state, - [otherField]: { - currencyId: undefined, - }, [field]: { currencyId: currencyId, }, @@ -62,6 +59,7 @@ export default createReducer(initialState, (builder) => return { ...state, independentField: state.independentField === Field.INPUT ? Field.OUTPUT : Field.INPUT, + typedValue: '', [Field.INPUT]: { currencyId: state[Field.OUTPUT].currencyId }, [Field.OUTPUT]: { currencyId: state[Field.INPUT].currencyId }, } diff --git a/src/state/transactions/actions.ts b/src/state/transactions/actions.ts index e39ac7af324..b5069f5e947 100644 --- a/src/state/transactions/actions.ts +++ b/src/state/transactions/actions.ts @@ -1,5 +1,5 @@ import { createAction } from '@reduxjs/toolkit' -import { ChainId } from '@ubeswap/sdk' +import { ChainId } from 'lib/token-utils' export interface SerializableTransactionReceipt { to: string diff --git a/src/state/transactions/updater.test.ts b/src/state/transactions/updater.test.ts deleted file mode 100644 index 45f5d46fdc1..00000000000 --- a/src/state/transactions/updater.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { shouldCheck } from './updater' - -describe('transactions updater', () => { - describe('shouldCheck', () => { - it('returns true if no receipt and never checked', () => { - expect(shouldCheck(10, { addedTime: 100 })).toEqual(true) - }) - it('returns false if has receipt and never checked', () => { - expect(shouldCheck(10, { addedTime: 100, receipt: {} })).toEqual(false) - }) - it('returns true if has not been checked in 1 blocks', () => { - expect(shouldCheck(10, { addedTime: new Date().getTime(), lastCheckedBlockNumber: 9 })).toEqual(true) - }) - it('returns false if checked in last 3 blocks and greater than 20 minutes old', () => { - expect(shouldCheck(10, { addedTime: new Date().getTime() - 21 * 60 * 1000, lastCheckedBlockNumber: 8 })).toEqual( - false - ) - }) - it('returns true if not checked in last 5 blocks and greater than 20 minutes old', () => { - expect(shouldCheck(10, { addedTime: new Date().getTime() - 21 * 60 * 1000, lastCheckedBlockNumber: 5 })).toEqual( - true - ) - }) - it('returns false if checked in last 10 blocks and greater than 60 minutes old', () => { - expect(shouldCheck(20, { addedTime: new Date().getTime() - 61 * 60 * 1000, lastCheckedBlockNumber: 11 })).toEqual( - false - ) - }) - it('returns true if checked in last 3 blocks and greater than 20 minutes old', () => { - expect(shouldCheck(20, { addedTime: new Date().getTime() - 61 * 60 * 1000, lastCheckedBlockNumber: 10 })).toEqual( - true - ) - }) - }) -}) diff --git a/src/state/transactions/updater.tsx b/src/state/transactions/updater.tsx index 610a78c020b..743d73a839f 100644 --- a/src/state/transactions/updater.tsx +++ b/src/state/transactions/updater.tsx @@ -50,6 +50,7 @@ export default function Updater(): null { .getTransactionReceipt(hash) .then((receipt) => { if (receipt) { + console.log('transaction update') dispatch( finalizeTransaction({ chainId: CHAIN, diff --git a/src/state/user/reducer.test.ts b/src/state/user/reducer.test.ts deleted file mode 100644 index 2e4aaf5c5b1..00000000000 --- a/src/state/user/reducer.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { createStore, Store } from 'redux' - -import { DEFAULT_DEADLINE_FROM_NOW, INITIAL_ALLOWED_SLIPPAGE } from '../../constants' -import { updateVersion } from '../global/actions' -import reducer, { initialState, UserState } from './reducer' - -describe('swap reducer', () => { - let store: Store - - beforeEach(() => { - store = createStore(reducer, initialState) - }) - - describe('updateVersion', () => { - it('has no timestamp originally', () => { - expect(store.getState().lastUpdateVersionTimestamp).toBeUndefined() - }) - it('sets the lastUpdateVersionTimestamp', () => { - const time = new Date().getTime() - store.dispatch(updateVersion()) - expect(store.getState().lastUpdateVersionTimestamp).toBeGreaterThanOrEqual(time) - }) - it('sets allowed slippage and deadline', () => { - store = createStore(reducer, { - ...initialState, - userDeadline: undefined, - userSlippageTolerance: undefined, - } as any) - store.dispatch(updateVersion()) - expect(store.getState().userDeadline).toEqual(DEFAULT_DEADLINE_FROM_NOW) - expect(store.getState().userSlippageTolerance).toEqual(INITIAL_ALLOWED_SLIPPAGE) - }) - }) -}) diff --git a/src/state/user/updater.tsx b/src/state/user/updater.tsx index b8e60b5932e..59b664cf740 100644 --- a/src/state/user/updater.tsx +++ b/src/state/user/updater.tsx @@ -14,6 +14,7 @@ export default function Updater(): null { } const match = window?.matchMedia('(prefers-color-scheme: dark)') + console.log('user update') dispatch(updateMatchesDarkMode({ matchesDarkMode: match.matches })) if (match?.addListener) { diff --git a/src/state/wallet/hooks.ts b/src/state/wallet/hooks.ts index a76e1c3bbd4..cccee295420 100644 --- a/src/state/wallet/hooks.ts +++ b/src/state/wallet/hooks.ts @@ -1,6 +1,7 @@ import { invariant } from '@apollo/client/utilities/globals' -import { JSBI, Token, TokenAmount } from '@ubeswap/sdk' import { getAllTokens } from 'hooks/Tokens' +import JSBI from 'jsbi' +import { Token, TokenAmount } from 'lib/token-utils' import { useEffect, useMemo, useState } from 'react' import { useBlockNumber } from 'state/application/hooks' @@ -38,24 +39,23 @@ export function useTokenBalances( () => tokens?.filter((t?: Token): t is Token => isAddress(t?.address) !== false) ?? [], [tokens] ) - const validatedTokenAddresses = useMemo(() => validatedTokens.map((vt) => vt.address), [validatedTokens]) const balances = useMultipleContractSingleData(validatedTokenAddresses, ERC20_INTERFACE, 'balanceOf', [address]) - - return useMemo( + const hold = useMemo( () => address && validatedTokens.length > 0 ? validatedTokens.reduce<{ [tokenAddress: string]: TokenAmount | undefined }>((memo, token, i) => { const value = balances?.[i]?.result?.[0] const amount = value ? JSBI.BigInt(value.toString()) : undefined if (amount) { - memo[token.address] = new TokenAmount(token, amount) + memo[token.address.toLowerCase()] = new TokenAmount(token, amount) } return memo }, {}) : {}, [address, validatedTokens, balances] ) + return hold } // mimics useAllBalances diff --git a/src/theme/components.tsx b/src/theme/components.tsx index a8470b01a6a..f38d065b13d 100644 --- a/src/theme/components.tsx +++ b/src/theme/components.tsx @@ -200,16 +200,6 @@ export const TrashIcon = styled(Trash)` } ` -const rotateImg = keyframes` - 0% { - transform: perspective(1000px) rotateY(0deg); - } - - 100% { - transform: perspective(1000px) rotateY(360deg); - } -` - export const UbeTokenAnimated = styled.img` padding: 2rem 0 1rem 0; filter: drop-shadow(0px 2px 4px rgba(0, 0, 0, 0.15)); diff --git a/src/utils/StablePairMath.ts b/src/utils/StablePairMath.ts deleted file mode 100644 index 6a2415d91fe..00000000000 --- a/src/utils/StablePairMath.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { JSBI } from '@ubeswap/sdk' -import BigNumber from 'bignumber.js' - -// See https://github.com/d-mooers/swappa/blob/main/src/pairs/stableswap.ts - -type Address = string -export class PairStableSwap { - allowRepeats = false - - private paused = false - private tokenPrecisionMultipliers: BigNumber[] = [] - private balancesWithAdjustedPrecision: BigNumber[] = [] - private balances: BigNumber[] = [] - private swapFee: BigNumber = new BigNumber(0) - private preciseA: BigNumber = new BigNumber(0) - private lpTotalSupply: BigNumber = new BigNumber(0) - - static readonly POOL_PRECISION_DECIMALS = 18 - static readonly A_PRECISION = 100 - - constructor(balances: string[], swapFee: string, preciseA: string, decimals: number[]) { - const [decimalsA, decimalsB] = decimals - this.tokenPrecisionMultipliers = [ - new BigNumber(10).pow(PairStableSwap.POOL_PRECISION_DECIMALS - decimalsA), - new BigNumber(10).pow(PairStableSwap.POOL_PRECISION_DECIMALS - decimalsB), - ] - this.balances = balances.map((n) => new BigNumber(n)) - this.balancesWithAdjustedPrecision = balances.map((b, idx) => this.tokenPrecisionMultipliers[idx].multipliedBy(b)) - this.swapFee = new BigNumber(swapFee).div(new BigNumber(10).pow(10)) - this.preciseA = new BigNumber(preciseA) - } - - public deposit( - tokenA: Address, - inputA: BigNumber, - tokenB: Address, - inputB: BigNumber, - isDeposit: boolean - ): BigNumber { - const amounts = tokenA === this.tokenA ? [inputA, inputB] : [inputB, inputA] - const d0 = this.getD(this.balancesWithAdjustedPrecision, this.preciseA) - const balances = this.balances.map((b, i) => (isDeposit ? b.plus(amounts[i]) : b.minus(amounts[i]))) - const d1 = this.getD( - balances.map((b, idx) => this.tokenPrecisionMultipliers[idx].multipliedBy(b)), - this.preciseA - ) - - return d1.minus(d0).multipliedBy(this.lpTotalSupply).dividedBy(d0) - } - - public outputAmount(tokenIndexFrom: number, tokenIndexTo: number, input: JSBI): [JSBI, JSBI] { - // See: https://github.com/mobiusAMM/mobiusV1/blob/master/contracts/SwapUtils.sol#L617 - const inputAmount = new BigNumber(input.toString()) - const x = inputAmount - .multipliedBy(this.tokenPrecisionMultipliers[tokenIndexFrom]) - .plus(this.balancesWithAdjustedPrecision[tokenIndexFrom]) - const y = this.getY(x, this.balancesWithAdjustedPrecision, this.preciseA) - const outputAmountWithFee = this.balancesWithAdjustedPrecision[tokenIndexTo].minus(y).minus(1) - const fee = outputAmountWithFee.multipliedBy(this.swapFee) - const outputAmount = outputAmountWithFee.minus(fee).div(this.tokenPrecisionMultipliers[tokenIndexTo]).integerValue() - return [JSBI.BigInt(outputAmount.toFixed(0)), JSBI.BigInt(fee.toFixed(0))] - } - - private getY = (x: BigNumber, xp: BigNumber[], a: BigNumber) => { - // See: https://github.com/mobiusAMM/mobiusV1/blob/master/contracts/SwapUtils.sol#L531 - const d = this.getD(xp, a) - const nTokens = xp.length - const nA = a.multipliedBy(nTokens) - - const s = x - const c = d - .multipliedBy(d) - .div(x.multipliedBy(nTokens)) - .integerValue() - .multipliedBy(d) - .multipliedBy(PairStableSwap.A_PRECISION) - .div(nA.multipliedBy(nTokens)) - .integerValue() - const b = s.plus(d.multipliedBy(PairStableSwap.A_PRECISION).div(nA)).integerValue() - - let yPrev - let y = d - for (let i = 0; i < 256; i++) { - yPrev = y - y = y.multipliedBy(y).plus(c).div(y.multipliedBy(2).plus(b).minus(d)).integerValue() - if (y.minus(yPrev).abs().lte(1)) { - return y - } - } - throw new Error('SwapPool approximation did not converge!') - } - - private getD(xp: BigNumber[], a: BigNumber) { - // See: https://github.com/mobiusAMM/mobiusV1/blob/master/contracts/SwapUtils.sol#L393 - const s = BigNumber.sum(...xp) - if (s.eq(0)) { - return s - } - - let prevD - let d = s - const nTokens = xp.length - const nA = a.multipliedBy(nTokens) - - for (let i = 0; i < 256; i++) { - let dP = d - xp.forEach((x) => { - dP = dP.multipliedBy(d).div(x.multipliedBy(nTokens)).integerValue() - }) - prevD = d - d = nA - .multipliedBy(s) - .div(PairStableSwap.A_PRECISION) - .plus(dP.multipliedBy(nTokens)) - .multipliedBy(d) - .div( - nA - .minus(PairStableSwap.A_PRECISION) - .multipliedBy(d) - .div(PairStableSwap.A_PRECISION) - .plus(new BigNumber(nTokens).plus(1).multipliedBy(dP)) - ) - .integerValue() - if (d.minus(prevD).abs().lte(1)) { - return d - } - } - throw new Error('SwapPool D does not converge!') - } -} diff --git a/src/utils/calcExpectedVeMobi.ts b/src/utils/calcExpectedVeMobi.ts index 85a7e3a6a34..760937252b1 100644 --- a/src/utils/calcExpectedVeMobi.ts +++ b/src/utils/calcExpectedVeMobi.ts @@ -1,20 +1,21 @@ -import { Fraction, JSBI, Token, TokenAmount } from '@ubeswap/sdk' +import JSBI from 'jsbi' +import { Fraction, Token, TokenAmount } from 'lib/token-utils' import { GaugeSummary } from 'state/staking/hooks' -export function calcBoost(summary: GaugeSummary, votingPower: JSBI, totalVotingPower: JSBI): Fraction { - const { baseBalance, totalStaked } = summary - const baseWeighted = JSBI.divide(JSBI.multiply(JSBI.BigInt(4), baseBalance.raw), JSBI.BigInt(10)) +// TODO double check that this math is correct +export function calcBoost(userBalance: JSBI, totalSupply: JSBI, votingPower: JSBI, totalVotingPower: JSBI): Fraction { + const baseWeighted = JSBI.divide(JSBI.multiply(JSBI.BigInt(4), userBalance), JSBI.BigInt(10)) let weighted = baseWeighted if (JSBI.greaterThan(totalVotingPower, JSBI.BigInt('0'))) { weighted = JSBI.add( - JSBI.divide(JSBI.multiply(JSBI.BigInt(4), baseBalance.raw), JSBI.BigInt(10)), + JSBI.divide(JSBI.multiply(JSBI.BigInt(4), userBalance), JSBI.BigInt(10)), JSBI.divide( - JSBI.multiply(JSBI.BigInt(6), JSBI.multiply(totalStaked.raw, votingPower)), + JSBI.multiply(JSBI.BigInt(6), JSBI.multiply(totalSupply, votingPower)), JSBI.multiply(totalVotingPower, JSBI.BigInt(10)) ) ) } - const min = JSBI.lessThan(weighted, baseBalance.raw) ? weighted : baseBalance.raw + const min = JSBI.lessThan(weighted, userBalance) ? weighted : userBalance return new Fraction(min, baseWeighted) } diff --git a/src/utils/calcRate.ts b/src/utils/calcRate.ts new file mode 100644 index 00000000000..bb8d154c91f --- /dev/null +++ b/src/utils/calcRate.ts @@ -0,0 +1,20 @@ +import JSBI from 'jsbi' +import { Fraction } from 'lib/token-utils' + +import { weiScale } from '../constants' + +type Rates = { + apr: Fraction + dpr: Fraction + apy: Fraction +} + +export function calcRates(rewardPerYear: Fraction, totalStakedAmount: Fraction): Rates { + const apr = !totalStakedAmount.equalTo(0) ? rewardPerYear.divide(totalStakedAmount) : new Fraction(0) + const dpr = apr.divide(365) + const apy = new Fraction( + JSBI.exponentiate(dpr.add(1).multiply(weiScale).quotient, JSBI.BigInt(364)), + JSBI.exponentiate(weiScale, JSBI.BigInt(364)) + ) + return { apr: apr.multiply(100), dpr: dpr.multiply(100), apy: apy.subtract(1).multiply(100) } +} diff --git a/src/utils/calculator/amounts.unit.test.ts b/src/utils/calculator/amounts.unit.test.ts deleted file mode 100644 index b85ad71a799..00000000000 --- a/src/utils/calculator/amounts.unit.test.ts +++ /dev/null @@ -1,275 +0,0 @@ -import type { BigintIsh } from '@saberhq/token-utils' -import { Percent, Token as SToken, TokenAmount } from '@saberhq/token-utils' -import { PublicKey } from '@solana/web3.js' -import BN from 'bn.js' -import JSBI from 'jsbi' -import mapValues from 'lodash.mapvalues' - -import { SWAP_PROGRAM_ID } from '../constants' -import type { IExchangeInfo } from '../entities/exchange' -import { RECOMMENDED_FEES, ZERO_FEES } from '../state/fees' -import { - calculateEstimatedMintAmount, - calculateEstimatedSwapOutputAmount, - calculateEstimatedWithdrawAmount, - calculateEstimatedWithdrawOneAmount, - calculateVirtualPrice, -} from './amounts' - -const exchange = { - swapAccount: new PublicKey('YAkoNb6HKmSxQN9L8hiBE5tPJRsniSSMzND1boHmZxe'), - programID: SWAP_PROGRAM_ID, - lpToken: new SToken({ - symbol: 'LP', - name: 'StableSwap LP', - address: '2poo1w1DL6yd2WNTCnNTzDqkC6MBXq7axo77P16yrBuf', - decimals: 6, - chainId: 100, - }), - tokens: [ - new SToken({ - symbol: 'TOKA', - name: 'Token A', - address: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', - decimals: 6, - chainId: 100, - }), - new SToken({ - symbol: 'TOKB', - name: 'Token B', - address: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', - decimals: 6, - chainId: 100, - }), - ], -} as const - -const makeExchangeInfo = ( - { - lpTotalSupply = JSBI.BigInt(200_000_000), - tokenAAmount = JSBI.BigInt(100_000_000), - tokenBAmount = JSBI.BigInt(100_000_000), - }: { - lpTotalSupply?: JSBI - tokenAAmount?: JSBI - tokenBAmount?: JSBI - } = { - lpTotalSupply: JSBI.BigInt(200_000_000), - tokenAAmount: JSBI.BigInt(100_000_000), - tokenBAmount: JSBI.BigInt(100_000_000), - } -): IExchangeInfo => ({ - ampFactor: JSBI.BigInt(100), - fees: ZERO_FEES, - lpTotalSupply: new TokenAmount(exchange.lpToken, lpTotalSupply), - reserves: [ - { - reserveAccount: new PublicKey('Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB'), - adminFeeAccount: new PublicKey('Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB'), - amount: new TokenAmount(exchange.tokens[0], tokenAAmount), - }, - { - reserveAccount: new PublicKey('Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB'), - adminFeeAccount: new PublicKey('Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB'), - amount: new TokenAmount(exchange.tokens[1], tokenBAmount), - }, - ], -}) - -const exchangeInfo = makeExchangeInfo() - -const exchangeInfoWithFees = { - ...exchangeInfo, - fees: RECOMMENDED_FEES, -} as const - -const assertTokenAmounts = (actual: TokenAmount, expected: TokenAmount) => { - expect(actual.equalTo(expected) && actual.token.equals(expected.token)).toBe(true) -} - -const assertTokenAmount = (actual: TokenAmount, expected: BigintIsh) => { - expect(actual.raw.toString()).toEqual(expected.toString()) -} - -describe('Calculated amounts', () => { - describe('#calculateVirtualPrice', () => { - it('works', () => { - const result = calculateVirtualPrice(exchangeInfo) - expect(result?.toFixed(4)).toBe('1.0000') - }) - - it('is symmetric', () => { - const result = calculateVirtualPrice( - makeExchangeInfo({ - lpTotalSupply: JSBI.BigInt(200_000_000), - tokenAAmount: JSBI.BigInt(10_000_000), - tokenBAmount: JSBI.BigInt(190_000_000), - }) - ) - expect(result?.toFixed(4)).toBe('0.9801') - - const result2 = calculateVirtualPrice( - makeExchangeInfo({ - lpTotalSupply: JSBI.BigInt(200_000_000), - tokenAAmount: JSBI.BigInt(190_000_000), - tokenBAmount: JSBI.BigInt(10_000_000), - }) - ) - expect(result2?.toFixed(4)).toBe('0.9801') - }) - - it('can quote both prices', () => { - const exchange = makeExchangeInfo({ - lpTotalSupply: JSBI.BigInt(200_000_000), - tokenAAmount: JSBI.BigInt(10_000_000), - tokenBAmount: JSBI.BigInt(190_000_000), - }) - - const result = calculateVirtualPrice(exchange) - expect(result?.toFixed(4)).toBe('0.9801') - }) - }) - - describe('#calculateEstimatedSwapOutputAmount', () => { - it('no fees', () => { - const result = calculateEstimatedSwapOutputAmount( - exchangeInfo, - new TokenAmount(exchange.tokens[0], JSBI.BigInt(10_000_000)) - ) - - assertTokenAmounts(result.outputAmount, result.outputAmountBeforeFees) - }) - - it('fees are different', () => { - const result = calculateEstimatedSwapOutputAmount( - { - ...exchangeInfoWithFees, - fees: { - ...exchangeInfoWithFees.fees, - trade: new Percent(50, 100), - }, - }, - new TokenAmount(exchange.tokens[0], JSBI.BigInt(100)) - ) - - // 50 percent fee - assertTokenAmount(result.outputAmountBeforeFees, JSBI.BigInt(100)) - assertTokenAmount(result.outputAmount, JSBI.BigInt(50)) - }) - }) - - describe('#calculateEstimatedMintAmount', () => { - it('no fees if equal liquidity provision', () => { - const result = calculateEstimatedMintAmount( - { - ...exchangeInfo, - fees: { - ...ZERO_FEES, - trade: new Percent(50, 100), - }, - }, - JSBI.BigInt(100), - JSBI.BigInt(100) - ) - - assertTokenAmounts(result.mintAmount, result.mintAmountBeforeFees) - }) - - it('fees if unequal liquidity provision', () => { - const result = calculateEstimatedMintAmount( - { - ...exchangeInfo, - fees: { - ...ZERO_FEES, - trade: new Percent(50, 100), - }, - }, - JSBI.BigInt(100_000), - JSBI.BigInt(0) - ) - - assertTokenAmount(result.mintAmountBeforeFees, new BN(99_999)) - // 3/4 because only half of the swapped amount (100 tokens) should have fees on it (so 1/4) - const expectedMintAmount = JSBI.divide( - JSBI.multiply(result.mintAmountBeforeFees.raw, JSBI.BigInt(3)), - JSBI.BigInt(4) - ) - assertTokenAmount(result.mintAmount, expectedMintAmount) - - assertTokenAmount(result.fees, JSBI.subtract(result.mintAmountBeforeFees.raw, expectedMintAmount)) - }) - }) - - describe('#calculateEstimatedWithdrawAmount', () => { - it('works', () => { - calculateEstimatedWithdrawAmount({ - ...exchangeInfo, - poolTokenAmount: new TokenAmount(exchange.lpToken, 100_000), - }) - }) - - it('works with fees', () => { - calculateEstimatedWithdrawAmount({ - ...exchangeInfoWithFees, - poolTokenAmount: new TokenAmount(exchange.lpToken, 100_000), - }) - }) - - it('works zero with fees', () => { - calculateEstimatedWithdrawAmount({ - ...exchangeInfoWithFees, - poolTokenAmount: new TokenAmount(exchange.lpToken, 0), - }) - }) - }) - - describe('#calculateEstimatedWithdrawOneAmount', () => { - it('works', () => { - calculateEstimatedWithdrawOneAmount({ - exchange: exchangeInfo, - poolTokenAmount: new TokenAmount(exchange.lpToken, 100_000), - withdrawToken: exchange.tokens[0], - }) - }) - - it('works with fees', () => { - const result = calculateEstimatedWithdrawOneAmount({ - exchange: exchangeInfoWithFees, - poolTokenAmount: new TokenAmount(exchange.lpToken, 100_000), - withdrawToken: exchange.tokens[0], - }) - - const resultMapped = mapValues(result, (q) => q.raw.toString()) - expect(resultMapped).toEqual({ - withdrawAmount: '99301', - withdrawAmountBeforeFees: '99900', - swapFee: '100', - withdrawFee: '500', - lpSwapFee: '50', - lpWithdrawFee: '250', - adminSwapFee: '50', - adminWithdrawFee: '250', - }) - }) - - it('works zero with fees', () => { - const result = calculateEstimatedWithdrawOneAmount({ - exchange: exchangeInfoWithFees, - poolTokenAmount: new TokenAmount(exchange.lpToken, 0), - withdrawToken: exchange.tokens[0], - }) - - const resultMapped = mapValues(result, (q) => q.raw.toString()) - expect(resultMapped).toEqual({ - withdrawAmount: '0', - withdrawAmountBeforeFees: '0', - swapFee: '0', - withdrawFee: '0', - lpSwapFee: '0', - lpWithdrawFee: '0', - adminSwapFee: '0', - adminWithdrawFee: '0', - }) - }) - }) -}) diff --git a/src/utils/calculator/curve.unit.test.ts b/src/utils/calculator/curve.unit.test.ts deleted file mode 100644 index 8eaf76c653a..00000000000 --- a/src/utils/calculator/curve.unit.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { BigintIsh } from '@saberhq/token-utils' -import JSBI from 'jsbi' - -import { computeD, computeY } from './curve' - -const assertBN = (actual: BigintIsh, expected: BigintIsh) => { - expect(actual.toString()).toEqual(expected.toString()) -} - -describe('Calculator tests', () => { - it('computeD', () => { - assertBN(computeD(JSBI.BigInt(100), JSBI.BigInt(0), JSBI.BigInt(0)), JSBI.BigInt(0)) - assertBN(computeD(JSBI.BigInt(100), JSBI.BigInt(1000000000), JSBI.BigInt(1000000000)), JSBI.BigInt(2000000000)) - assertBN(computeD(JSBI.BigInt(73), JSBI.BigInt(92), JSBI.BigInt(81)), JSBI.BigInt(173)) - assertBN(computeD(JSBI.BigInt(11503), JSBI.BigInt(28338), JSBI.BigInt(78889)), JSBI.BigInt(107225)) - assertBN(computeD(JSBI.BigInt(8552), JSBI.BigInt(26), JSBI.BigInt(69321)), JSBI.BigInt(66920)) - assertBN(computeD(JSBI.BigInt(496), JSBI.BigInt(62), JSBI.BigInt(68567)), JSBI.BigInt(57447)) - assertBN( - computeD( - JSBI.BigInt('17653203515214796177'), - JSBI.BigInt('13789683482691983066'), - JSBI.BigInt('3964443602730479576') - ), - JSBI.BigInt('17754127085422462641') - ) - }) - - it('computeY', () => { - assertBN(computeY(JSBI.BigInt(100), JSBI.BigInt(100), JSBI.BigInt(0)), JSBI.BigInt(0)) - assertBN(computeY(JSBI.BigInt(8), JSBI.BigInt(94), JSBI.BigInt(163)), JSBI.BigInt(69)) - assertBN(computeY(JSBI.BigInt(2137), JSBI.BigInt(905777403660), JSBI.BigInt(830914146046)), JSBI.BigInt(490376033)) - assertBN( - computeY(JSBI.BigInt('17095344176474858097'), JSBI.BigInt(383), JSBI.BigInt('2276818911077272163')), - JSBI.BigInt('2276917873767753112') - ) - assertBN( - computeY( - JSBI.BigInt('7644937799120520965'), - JSBI.BigInt('14818904982296505121'), - JSBI.BigInt('17480022366793075404') - ), - JSBI.BigInt('2661117384496570284') - ) - }) -}) diff --git a/src/utils/chunkArray.test.ts b/src/utils/chunkArray.test.ts deleted file mode 100644 index 34b9bc5698b..00000000000 --- a/src/utils/chunkArray.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import chunkArray from './chunkArray' - -describe('#chunkArray', () => { - it('size 1', () => { - expect(chunkArray([1, 2, 3], 1)).toEqual([[1], [2], [3]]) - }) - it('size 0 throws', () => { - expect(() => chunkArray([1, 2, 3], 0)).toThrow('maxChunkSize must be gte 1') - }) - it('size gte items', () => { - expect(chunkArray([1, 2, 3], 3)).toEqual([[1, 2, 3]]) - expect(chunkArray([1, 2, 3], 4)).toEqual([[1, 2, 3]]) - }) - it('size exact half', () => { - expect(chunkArray([1, 2, 3, 4], 2)).toEqual([ - [1, 2], - [3, 4], - ]) - }) - it('evenly distributes', () => { - const chunked = chunkArray([...Array(100).keys()], 40) - - expect(chunked).toEqual([ - [...Array(34).keys()], - [...Array(34).keys()].map((i) => i + 34), - [...Array(32).keys()].map((i) => i + 68), - ]) - - expect(chunked[0][0]).toEqual(0) - expect(chunked[2][31]).toEqual(99) - }) -}) diff --git a/src/utils/currencyId.ts b/src/utils/currencyId.ts deleted file mode 100644 index 2de6c5dfa0a..00000000000 --- a/src/utils/currencyId.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Token } from '@ubeswap/sdk' - -export function currencyId(currency: Token): string { - return currency.address -} diff --git a/src/utils/eth.ts b/src/utils/eth.ts deleted file mode 100644 index 7a0547c5f55..00000000000 --- a/src/utils/eth.ts +++ /dev/null @@ -1,8 +0,0 @@ -import BN from 'bn.js' -import { fromWei } from 'web3-utils' - -import { humanFriendlyNumber } from './number' - -export const humanFriendlyWei = (wei: BN | string) => { - return humanFriendlyNumber(fromWei(wei)) -} diff --git a/src/utils/index.ts b/src/utils/index.ts index 8bf61308a95..746d47d33af 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -3,8 +3,9 @@ import { BigNumber } from '@ethersproject/bignumber' import { AddressZero } from '@ethersproject/constants' import { Contract } from '@ethersproject/contracts' import { JsonRpcSigner, Web3Provider } from '@ethersproject/providers' -import { JSBI, Percent, TokenAmount } from '@ubeswap/sdk' import { Exchange, Swap } from 'generated/index' +import JSBI from 'jsbi' +import { Percent } from 'lib/token-utils' import EXCHANGE from '../constants/abis/Exchange.json' import SWAP from '../constants/abis/Swap.json' @@ -37,16 +38,6 @@ export function basisPointsToPercent(num: number): Percent { return new Percent(JSBI.BigInt(num), JSBI.BigInt(10000)) } -export function calculateSlippageAmount(value: TokenAmount, slippage: number): [JSBI, JSBI] { - if (slippage < 0 || slippage > 10000) { - throw Error(`Unexpected slippage value: ${slippage}`) - } - return [ - JSBI.divide(JSBI.multiply(value.raw, JSBI.BigInt(10000 - slippage)), JSBI.BigInt(10000)), - JSBI.divide(JSBI.multiply(value.raw, JSBI.BigInt(10000 + slippage)), JSBI.BigInt(10000)), - ] -} - // account is not optional export function getSigner(provider: Web3Provider): JsonRpcSigner { return provider.getSigner() diff --git a/src/utils/listSort.ts b/src/utils/listSort.ts deleted file mode 100644 index 7672da194dd..00000000000 --- a/src/utils/listSort.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { DEFAULT_LIST_OF_LISTS } from './../constants/lists' - -// use ordering of default list of lists to assign priority -export default function sortByListPriority(urlA: string, urlB: string) { - const first = DEFAULT_LIST_OF_LISTS.includes(urlA) ? DEFAULT_LIST_OF_LISTS.indexOf(urlA) : Number.MAX_SAFE_INTEGER - const second = DEFAULT_LIST_OF_LISTS.includes(urlB) ? DEFAULT_LIST_OF_LISTS.indexOf(urlB) : Number.MAX_SAFE_INTEGER - - // need reverse order to make sure mapping includes top priority last - if (first < second) return 1 - else if (first > second) return -1 - return 0 -} diff --git a/src/utils/maxAmountSpend.ts b/src/utils/maxAmountSpend.ts index dec67173b59..c2032379152 100644 --- a/src/utils/maxAmountSpend.ts +++ b/src/utils/maxAmountSpend.ts @@ -1,4 +1,4 @@ -import { TokenAmount } from '@ubeswap/sdk' +import { TokenAmount } from 'lib/token-utils' /** * Given some token amount, return the max that can be spent of it diff --git a/src/utils/parseENSAddress.test.ts b/src/utils/parseENSAddress.test.ts deleted file mode 100644 index 975449b6017..00000000000 --- a/src/utils/parseENSAddress.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { parseENSAddress } from './parseENSAddress' - -describe('parseENSAddress', () => { - it('test cases', () => { - expect(parseENSAddress('hello.eth')).toEqual({ ensName: 'hello.eth', ensPath: undefined }) - expect(parseENSAddress('hello.eth/')).toEqual({ ensName: 'hello.eth', ensPath: '/' }) - expect(parseENSAddress('hello.world.eth/')).toEqual({ ensName: 'hello.world.eth', ensPath: '/' }) - expect(parseENSAddress('hello.world.eth/abcdef')).toEqual({ ensName: 'hello.world.eth', ensPath: '/abcdef' }) - expect(parseENSAddress('abso.lutely')).toEqual(undefined) - expect(parseENSAddress('abso.lutely.eth')).toEqual({ ensName: 'abso.lutely.eth', ensPath: undefined }) - expect(parseENSAddress('eth')).toEqual(undefined) - expect(parseENSAddress('eth/hello-world')).toEqual(undefined) - expect(parseENSAddress('hello-world.eth')).toEqual({ ensName: 'hello-world.eth', ensPath: undefined }) - expect(parseENSAddress('-prefix-dash.eth')).toEqual(undefined) - expect(parseENSAddress('suffix-dash-.eth')).toEqual(undefined) - expect(parseENSAddress('it.eth')).toEqual({ ensName: 'it.eth', ensPath: undefined }) - expect(parseENSAddress('only-single--dash.eth')).toEqual(undefined) - }) -}) diff --git a/src/utils/prices.ts b/src/utils/prices.ts index 019186d039c..211e48a7459 100644 --- a/src/utils/prices.ts +++ b/src/utils/prices.ts @@ -1,4 +1,5 @@ -import { JSBI, Percent, TokenAmount } from '@ubeswap/sdk' +import JSBI from 'jsbi' +import { Percent, TokenAmount } from 'lib/token-utils' import { MentoTrade } from 'state/mento/hooks' import { MobiusTrade } from 'state/swap/hooks' @@ -24,8 +25,8 @@ export function computeSlippageAdjustedAmounts( const maxInput = JSBI.add(inputRaw, JSBI.divide(JSBI.multiply(inputRaw, pct.numerator), pct.denominator)) const minOutput = JSBI.subtract(outputRaw, JSBI.divide(JSBI.multiply(outputRaw, pct.numerator), pct.denominator)) return { - [Field.INPUT]: new TokenAmount(trade?.input.currency, maxInput), - [Field.OUTPUT]: new TokenAmount(trade?.output.currency, minOutput), + [Field.INPUT]: new TokenAmount(trade?.input.token, maxInput), + [Field.OUTPUT]: new TokenAmount(trade?.output.token, minOutput), } } @@ -42,8 +43,6 @@ export function formatExecutionPrice(trade?: MobiusTrade | MentoTrade, inverted? return '' } return inverted - ? `${trade.executionPrice.invert().toSignificant(6)} ${trade.input.currency.symbol} / ${ - trade.output.currency.symbol - }` - : `${trade.executionPrice.toSignificant(6)} ${trade.output.currency.symbol} / ${trade.input.currency.symbol}` + ? `${trade.executionPrice.invert().toSignificant(6)} ${trade.input.token.symbol} / ${trade.output.token.symbol}` + : `${trade.executionPrice.toSignificant(6)} ${trade.output.token.symbol} / ${trade.input.token.symbol}` } diff --git a/src/utils/retry.test.ts b/src/utils/retry.test.ts deleted file mode 100644 index fe1952ac320..00000000000 --- a/src/utils/retry.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { retry, RetryableError } from './retry' - -describe('retry', () => { - function makeFn(fails: number, result: T, retryable = true): () => Promise { - return async () => { - if (fails > 0) { - fails-- - throw retryable ? new RetryableError('failure') : new Error('bad failure') - } - return result - } - } - - it('fails for non-retryable error', async () => { - await expect(retry(makeFn(1, 'abc', false), { n: 3, maxWait: 0, minWait: 0 }).promise).rejects.toThrow( - 'bad failure' - ) - }) - - it('works after one fail', async () => { - await expect(retry(makeFn(1, 'abc'), { n: 3, maxWait: 0, minWait: 0 }).promise).resolves.toEqual('abc') - }) - - it('works after two fails', async () => { - await expect(retry(makeFn(2, 'abc'), { n: 3, maxWait: 0, minWait: 0 }).promise).resolves.toEqual('abc') - }) - - it('throws if too many fails', async () => { - await expect(retry(makeFn(4, 'abc'), { n: 3, maxWait: 0, minWait: 0 }).promise).rejects.toThrow('failure') - }) - - it('cancel causes promise to reject', async () => { - const { promise, cancel } = retry(makeFn(2, 'abc'), { n: 3, minWait: 100, maxWait: 100 }) - cancel() - await expect(promise).rejects.toThrow('Cancelled') - }) - - it('cancel no-op after complete', async () => { - const { promise, cancel } = retry(makeFn(0, 'abc'), { n: 3, minWait: 100, maxWait: 100 }) - // defer - setTimeout(cancel, 0) - await expect(promise).resolves.toEqual('abc') - }) - - async function checkTime(fn: () => Promise, min: number, max: number) { - const time = new Date().getTime() - await fn() - const diff = new Date().getTime() - time - expect(diff).toBeGreaterThanOrEqual(min) - expect(diff).toBeLessThanOrEqual(max) - } - - it('waits random amount of time between min and max', async () => { - const promises = [] - for (let i = 0; i < 10; i++) { - promises.push( - checkTime( - () => expect(retry(makeFn(4, 'abc'), { n: 3, maxWait: 100, minWait: 50 }).promise).rejects.toThrow('failure'), - 150, - 400 - ) - ) - } - await Promise.all(promises) - }) -}) diff --git a/src/utils/stableSwapMath.ts b/src/utils/stableSwapMath.ts deleted file mode 100644 index 371ff2673f3..00000000000 --- a/src/utils/stableSwapMath.ts +++ /dev/null @@ -1,349 +0,0 @@ -import { BigintIsh, JSBI } from '@ubeswap/sdk' -import { StableSwapMathConstants, StableSwapVariable } from 'state/stablePools/reducer' - -const ZERO = JSBI.BigInt(0) -const ONE = JSBI.BigInt(1) -const TWO = JSBI.BigInt(2) -export class StableSwapMath { - public readonly RATES: JSBI[] - public readonly LENDING_PRECISION: JSBI - public readonly PRECISION: JSBI - public readonly FEE_DENOMINATOR: JSBI - public readonly PRECISION_MUL: JSBI[] - public readonly N_COINS: number - public readonly FEE_INDEX: number - public readonly DECIMALS: JSBI[] - public readonly POOL_PRECISION_DECIMALS = JSBI.BigInt('18') - public readonly tokenPrecisionMultipliers: JSBI[] - public readonly MAX_LOOP_LIMIT = 256 - public readonly A_PRECISION = JSBI.BigInt('100') - - public lpTotalSupply: JSBI - public swapFee: JSBI - public currentWithdrawFee: JSBI - public balances: JSBI[] - public amp: JSBI - public D: JSBI | undefined - public xp: JSBI[] | undefined - public aPrecise: JSBI - - constructor({ - rates, - lendingPrecision, - precision, - feeDenominator, - precisionMul, - feeIndex, - decimals, - amp, - balances, - lpTotalSupply, - swapFee, - aPrecise, - }: StableSwapMathConstants & StableSwapVariable) { - this.RATES = rates - this.LENDING_PRECISION = lendingPrecision - this.PRECISION = precision - this.FEE_DENOMINATOR = feeDenominator - this.PRECISION_MUL = precisionMul - this.N_COINS = rates.length - this.FEE_INDEX = feeIndex - this.DECIMALS = decimals - const tokenPrecisionMultipliers = decimals.map((deci) => - JSBI.exponentiate(JSBI.BigInt('10'), JSBI.subtract(this.POOL_PRECISION_DECIMALS, deci)) - ) - - this.tokenPrecisionMultipliers = tokenPrecisionMultipliers - - this.currentWithdrawFee = ZERO - this.swapFee = ONE - this.amp = ONE - this.balances = Array(this.N_COINS).fill(ZERO) - this.lpTotalSupply = ONE - this.aPrecise = aPrecise - this.updateInfo(swapFee, amp, balances, lpTotalSupply) - } - - updateInfo(swapFee: JSBI, amp: JSBI, balances: JSBI[], lpTotalSupply: JSBI) { - this.swapFee = swapFee - this.amp = amp - this.balances = balances - this.lpTotalSupply = lpTotalSupply - } - - calc_xp_mem(balances: BigintIsh[]): JSBI[] { - const xp: JSBI[] = new Array(this.N_COINS) - for (let i = 0; i < this.N_COINS; i += 1) { - xp[i] = JSBI.multiply(JSBI.BigInt(balances[i].toString()), this.tokenPrecisionMultipliers[i]) - } - this.xp = xp - return xp - } - - calc_xp(): JSBI[] { - return this.calc_xp_mem(this.balances) - } - - calc_D_xp(xp: JSBI[], amp: JSBI): JSBI { - // const S = xp.reduce((accum, cur) => JSBI.add(accum, cur)) - const N_COINS = JSBI.BigInt(this.N_COINS) - let S = ZERO - for (let i = 0; i < this.N_COINS; i += 1) { - S = JSBI.add(S, xp[i]) - } - if (JSBI.equal(S, ZERO)) return ZERO - - let Dprev = ZERO - let D = S - const na = JSBI.multiply(amp, N_COINS) - - for (let i = 0; i < 255; i++) { - // const D_P = xp.reduce((accum, cur) => JSBI.divide(JSBI.multiply(accum, D), JSBI.multiply(cur, N_COINS)), D) - let dP = D - for (let j = 0; j < this.N_COINS; j += 1) { - dP = JSBI.divide(JSBI.multiply(dP, D), JSBI.multiply(xp[j], N_COINS)) - } - Dprev = D - // const left = JSBI.multiply(JSBI.add(JSBI.multiply(Ann, S), JSBI.multiply(D_P, N_COINS)), D) - const left = JSBI.multiply( - JSBI.add(JSBI.divide(JSBI.multiply(na, S), this.A_PRECISION), JSBI.multiply(dP, N_COINS)), - D - ) - - const right = JSBI.add( - JSBI.divide(JSBI.multiply(JSBI.subtract(na, this.A_PRECISION), D), this.A_PRECISION), - JSBI.multiply(JSBI.add(N_COINS, ONE), dP) - ) - - D = JSBI.divide(left, right) - - if (JSBI.greaterThan(D, Dprev)) { - if (JSBI.lessThanOrEqual(JSBI.subtract(D, Dprev), ONE)) { - break - } - } else { - if (JSBI.lessThanOrEqual(JSBI.subtract(Dprev, D), ONE)) { - break - } - } - } - this.D = D - return D - } - - calc_D(): JSBI { - return this.calc_D_xp(this.calc_xp_mem(this.balances), JSBI.multiply(this.amp, this.A_PRECISION)) - } - - getD(xp: JSBI[], amp: JSBI): JSBI { - const N_COINS = JSBI.BigInt(this.N_COINS) - let S = ZERO - for (let i = 0; i < this.N_COINS; i += 1) { - S = JSBI.add(S, xp[i]) - } - if (JSBI.equal(S, ZERO)) return ZERO - - let Dprev = ZERO - let D = S - const na = JSBI.multiply(amp, N_COINS) - - for (let i = 0; i < 255; i++) { - let dP = D - for (let j = 0; j < this.N_COINS; j += 1) { - dP = JSBI.divide(JSBI.multiply(dP, D), JSBI.multiply(xp[j], N_COINS)) - } - Dprev = D - const left = JSBI.multiply( - JSBI.add(JSBI.divide(JSBI.multiply(na, S), this.A_PRECISION), JSBI.multiply(dP, N_COINS)), - D - ) - const right = JSBI.add( - JSBI.divide(JSBI.multiply(JSBI.subtract(na, this.A_PRECISION), D), this.A_PRECISION), - JSBI.multiply(JSBI.add(N_COINS, ONE), dP) - ) - D = JSBI.divide(left, right) - - if (JSBI.greaterThan(D, Dprev)) { - if (JSBI.lessThanOrEqual(JSBI.subtract(D, Dprev), ONE)) { - break - } - } else { - if (JSBI.lessThanOrEqual(JSBI.subtract(Dprev, D), ONE)) { - break - } - } - } - this.D = D - return D - } - - getY(indexFrom: number, indexTo: number, x: JSBI, xp: JSBI[]): JSBI { - const N_COINS = JSBI.BigInt(this.N_COINS) - const a = JSBI.multiply(this.amp, this.A_PRECISION) - const d = this.getD(xp, a) - let c = d - let s = ZERO - const na = JSBI.multiply(a, N_COINS) - - let _x = ZERO - for (let i = 0; i < this.N_COINS; i += 1) { - if (i === indexFrom) { - _x = x - } else if (i !== indexTo) { - _x = xp[i] - } else { - continue - } - s = JSBI.add(s, _x) - c = JSBI.divide(JSBI.multiply(c, d), JSBI.multiply(_x, N_COINS)) - } - c = JSBI.divide(JSBI.multiply(this.A_PRECISION, JSBI.multiply(c, d)), JSBI.multiply(na, N_COINS)) - const b = JSBI.add(s, JSBI.divide(JSBI.multiply(d, this.A_PRECISION), na)) - let y_prev = ZERO - let y = d - - for (let _i = 0; _i < 255; _i += 1) { - y_prev = y - y = JSBI.divide(JSBI.add(JSBI.multiply(y, y), c), JSBI.subtract(JSBI.add(JSBI.multiply(TWO, y), b), d)) - - if (JSBI.greaterThan(y, y_prev)) { - if (JSBI.lessThanOrEqual(JSBI.subtract(y, y_prev), ONE)) { - return y - } - } else { - if (JSBI.lessThanOrEqual(JSBI.subtract(y_prev, y), ONE)) { - return y - } - } - } - return y - } - - calculateSwap(indexFrom: number, indexTo: number, dx: JSBI, xp: JSBI[]): [JSBI, JSBI] { - const x = JSBI.add(xp[indexFrom], JSBI.multiply(this.tokenPrecisionMultipliers[indexFrom], dx)) - const y = this.getY(indexFrom, indexTo, x, xp) - let dy = JSBI.subtract(JSBI.subtract(xp[indexTo], y), ONE) - const dyFee = JSBI.divide(JSBI.multiply(dy, this.swapFee), this.FEE_DENOMINATOR) - dy = JSBI.divide(JSBI.subtract(dy, dyFee), this.tokenPrecisionMultipliers[indexTo]) - return [dy, dyFee] - } - - calculateRemoveLiquidity(amount: JSBI, lpTotalSupply: JSBI) { - const feeAdjustedAmount = JSBI.divide( - JSBI.multiply(JSBI.subtract(this.FEE_DENOMINATOR, this.currentWithdrawFee), amount), - this.FEE_DENOMINATOR - ) - const amounts = this.balances.map((bal) => JSBI.divide(JSBI.multiply(bal, feeAdjustedAmount), lpTotalSupply)) - return amounts - } - - _feePerToken(swapFee: JSBI, numTokens: JSBI): JSBI { - return JSBI.divide( - JSBI.multiply(swapFee, numTokens), - JSBI.multiply(JSBI.subtract(numTokens, JSBI.BigInt(1)), JSBI.BigInt(4)) - ) - } - - calculateWithdrawOneTokenDY(index: number, amount: JSBI): [JSBI, JSBI, JSBI] { - const xp = this.calc_xp() - const preciseA = this.aPrecise //JSBI.multiply(this.amp, this.A_PRECISION) - const d0 = this.getD(xp, preciseA) - const d1 = JSBI.subtract(d0, JSBI.divide(JSBI.multiply(amount, d0), this.lpTotalSupply)) - - const newY = this.getYD(preciseA, index, xp, d1) - const xpReduced: JSBI[] = new Array(xp.length).fill(JSBI.BigInt('0')) - const feePerToken = this._feePerToken(this.swapFee, JSBI.BigInt(xp.length)) - for (let i = 0; i < xp.length; i++) { - const xpi = xp[i] - const toSubtract = - i === index - ? JSBI.subtract(JSBI.divide(JSBI.multiply(xpi, d1), d0), newY) - : JSBI.subtract(xpi, JSBI.divide(JSBI.multiply(xpi, d1), d0)) - xpReduced[i] = JSBI.subtract(xpi, JSBI.divide(JSBI.multiply(toSubtract, feePerToken), this.FEE_DENOMINATOR)) - } - const y = this.getYD(preciseA, index, xpReduced, d1) - let dy = JSBI.subtract(xpReduced[index], y) - dy = JSBI.divide(JSBI.subtract(dy, JSBI.BigInt(1)), this.tokenPrecisionMultipliers[index]) - return [dy, newY, xp[index]] - } - - getYD(a: JSBI, index: number, xp: JSBI[], d: JSBI): JSBI { - const numTokens = xp.length - let c: JSBI = d - let s: JSBI = JSBI.BigInt(0) - const nA: JSBI = JSBI.multiply(a, JSBI.BigInt(numTokens)) - for (let i = 0; i < numTokens; i += 1) { - if (i !== index) { - s = JSBI.add(s, xp[i]) - c = JSBI.divide(JSBI.multiply(c, d), JSBI.multiply(xp[i], JSBI.BigInt(numTokens))) - } - } - c = JSBI.divide(JSBI.multiply(JSBI.multiply(c, d), this.A_PRECISION), JSBI.multiply(nA, JSBI.BigInt(numTokens))) - - const b = JSBI.add(s, JSBI.divide(JSBI.multiply(d, this.A_PRECISION), nA)) - let yPrev: JSBI - let y = d - - for (let _i = 0; _i < 255; _i += 1) { - yPrev = y - y = JSBI.divide(JSBI.add(JSBI.multiply(y, y), c), JSBI.subtract(JSBI.add(JSBI.multiply(TWO, y), b), d)) - // console.log({ y: y.toString(), yPrev: yPrev.toString() }) - if (JSBI.greaterThan(y, yPrev)) { - if (JSBI.lessThanOrEqual(JSBI.subtract(y, yPrev), ONE)) { - return y - } - } else { - if (JSBI.lessThanOrEqual(JSBI.subtract(yPrev, y), ONE)) { - return y - } - } - } - console.error('Approximation did not converge') - return y - } - - calculateWithdrawOneToken(index: number, amount: JSBI): [JSBI, JSBI] { - const [dy, newY, currentY] = this.calculateWithdrawOneTokenDY(index, amount) - const swapFee = JSBI.subtract(JSBI.divide(JSBI.subtract(currentY, newY), this.tokenPrecisionMultipliers[index]), dy) - return [dy, swapFee] - } - - calculateTokenAmount(originalAmounts: JSBI[], deposit: boolean): JSBI { - const amounts = originalAmounts.map((a) => (JSBI.equal(a, ZERO) ? JSBI.BigInt('1') : a)) - const a = this.aPrecise - const d0 = this.getD(this.calc_xp(), a) - const balances1 = this.balances.map((bal, i) => - deposit ? JSBI.add(bal, amounts[i]) : JSBI.subtract(bal, amounts[i]) - ) - const d1 = this.getD(this.calc_xp_mem(balances1), a) - const totalSupply = this.lpTotalSupply - if (deposit) { - const expected = JSBI.divide(JSBI.multiply(JSBI.subtract(d1, d0), totalSupply), d0) - return expected - } else { - return JSBI.divide( - JSBI.multiply(JSBI.divide(JSBI.multiply(JSBI.subtract(d0, d1), totalSupply), d0), this.FEE_DENOMINATOR), - this.FEE_DENOMINATOR - ) - } - } - - get_dx(i: number, j: number, dy: JSBI, xp: JSBI[]): JSBI { - const y: JSBI = JSBI.subtract( - xp[j], - JSBI.divide( - JSBI.multiply( - JSBI.divide( - JSBI.multiply(dy, this.FEE_DENOMINATOR), - JSBI.subtract(this.FEE_DENOMINATOR, JSBI.BigInt(this.FEE_INDEX.toString())) - ), - this.RATES[j] - ), - this.PRECISION - ) - ) - const x: JSBI = this.getY(j, i, y, xp) - const dx: JSBI = JSBI.divide(JSBI.multiply(JSBI.subtract(x, xp[i]), this.PRECISION), this.RATES[i]) - return dx - } -} diff --git a/src/utils/stableSwaps.ts b/src/utils/stableSwaps.ts deleted file mode 100644 index 1c6a27754f2..00000000000 --- a/src/utils/stableSwaps.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { JSBI, Token, TokenAmount } from '@ubeswap/sdk' - -import { StablePoolInfo } from '../state/stablePools/hooks' - -const dummyToken = new Token(1, '0x8cD0E2F11ed2E896a8307280dEEEE15B27e46BbE', 18, 'MobLP', 'Mobius cBTC/wBTC LP') -const dummyAmount = new TokenAmount(dummyToken, '0') -const WEI_SCALE = JSBI.exponentiate(JSBI.BigInt('10'), JSBI.BigInt('18')) - -const scaleAmount = - (toScale: JSBI) => - (ta: TokenAmount | undefined, addition = JSBI.BigInt('0')) => - toScale && ta - ? new TokenAmount(ta.token, JSBI.divide(JSBI.multiply(JSBI.add(addition, ta.raw), toScale), WEI_SCALE)) - : new TokenAmount(dummyToken, '0') - -export const getDepositValues = ( - pool: StablePoolInfo | undefined, - workingSupply?: JSBI -): { - valueOfStaked: TokenAmount - valueOfDeposited: TokenAmount - totalValueDeposited: TokenAmount - totalValueStaked: TokenAmount -} => { - if (!pool) - return { - valueOfDeposited: dummyAmount, - valueOfStaked: dummyAmount, - totalValueDeposited: dummyAmount, - totalValueStaked: dummyAmount, - } - const { totalDeposited, amountDeposited, virtualPrice, stakedAmount, totalStakedAmount } = pool - const scale = scaleAmount(virtualPrice) - - const valueOfStaked = scale(stakedAmount) - const valueOfDeposited = scale(amountDeposited) //?.add(stakedAmount)) - const totalValueStaked = workingSupply - ? scale(totalStakedAmount?.add(new TokenAmount(totalStakedAmount.token, workingSupply))) - : scale(totalStakedAmount) - const totalValueDeposited = scale(totalDeposited) - return { - valueOfStaked, - valueOfDeposited, - totalValueDeposited, - totalValueStaked, - } -} diff --git a/src/utils/tokens.ts b/src/utils/tokens.ts index 0314450fb8a..015a8b2599e 100644 --- a/src/utils/tokens.ts +++ b/src/utils/tokens.ts @@ -1,4 +1,4 @@ -import { Token } from '@ubeswap/sdk' +import { Token } from 'lib/token-utils' /** * Dedupes a list of tokens, picking the first instance of the token in a list. * @param tokens diff --git a/src/utils/uriToHttp.test.ts b/src/utils/uriToHttp.test.ts deleted file mode 100644 index 8393a7bcf93..00000000000 --- a/src/utils/uriToHttp.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import uriToHttp from './uriToHttp' - -describe('uriToHttp', () => { - it('returns .eth.link for ens names', () => { - expect(uriToHttp('t2crtokens.eth')).toEqual([]) - }) - it('returns https first for http', () => { - expect(uriToHttp('http://test.com')).toEqual(['https://test.com', 'http://test.com']) - }) - it('returns https for https', () => { - expect(uriToHttp('https://test.com')).toEqual(['https://test.com']) - }) - it('returns ipfs gateways for ipfs:// urls', () => { - expect(uriToHttp('ipfs://QmV8AfDE8GFSGQvt3vck8EwAzsPuNTmtP8VcQJE3qxRPaZ')).toEqual([ - 'https://cloudflare-ipfs.com/ipfs/QmV8AfDE8GFSGQvt3vck8EwAzsPuNTmtP8VcQJE3qxRPaZ/', - 'https://ipfs.io/ipfs/QmV8AfDE8GFSGQvt3vck8EwAzsPuNTmtP8VcQJE3qxRPaZ/', - ]) - }) - it('returns ipns gateways for ipns:// urls', () => { - expect(uriToHttp('ipns://app.ubeswap.org')).toEqual([ - 'https://cloudflare-ipfs.com/ipns/app.ubeswap.org/', - 'https://ipfs.io/ipns/app.ubeswap.org/', - ]) - }) - it('returns empty array for invalid scheme', () => { - expect(uriToHttp('blah:test')).toEqual([]) - }) -}) diff --git a/src/utils/useCUSDPrice.ts b/src/utils/useCUSDPrice.ts deleted file mode 100644 index 824039c76a3..00000000000 --- a/src/utils/useCUSDPrice.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { cUSD, Fraction, Price, Token } from '@ubeswap/sdk' -import { TokenPrices } from 'state/application/reducer' - -import { CHAIN } from '../constants' -import { priceStringToFraction, useTokenPrice } from '../state/application/hooks' - -type TokenPair = [Token | undefined, Token | undefined] - -export function getCUSDPrices(prices?: TokenPrices): { [address: string]: Fraction } { - return ( - Object.entries(prices ?? {})?.reduce( - (accum, [address, price]) => ({ ...accum, [address]: priceStringToFraction(price) }), - {} - ) ?? {} - ) -} - -/** - * Returns the price in cUSD of the input currency - * @param currency currency to compute the cUSD price of - */ -export function useCUSDPrice(tokens?: Token[] | Token): Price | undefined { - const CUSD = cUSD[CHAIN] - if (tokens instanceof Token) { - tokens = [tokens] - } - - const p1 = useTokenPrice(tokens?.[0]?.address) - const p2 = useTokenPrice(tokens?.[1]?.address) - return p1 - ? new Price(tokens?.[0], CUSD, p1.denominator, p1.numerator) - : p2 - ? new Price(tokens?.[1], CUSD, p2.denominator, p2.numerator) - : undefined - - // const CUSD = cUSD[chainId as unknown as UbeswapChainId] - // const celo = CELO[chainId as unknown as UbeswapChainId] - // const tokenPairs: TokenPair[] = useMemo( - // () => - // tokens - // ?.map((token) => [ - // [token && currencyEquals(token, CUSD) ? undefined : token, CUSD], - // [token && currencyEquals(token, celo) ? undefined : token, celo], - // [celo, CUSD], - // ]) - // .flat() as TokenPair[], - // [CUSD, celo, tokens] - // ) - // const pairs = usePairs(tokenPairs).map((x) => x[1]) - - // return useMemo(() => { - // if (!tokens || !chainId) { - // return undefined - // } - - // const prices = tokens.map((token, idx) => { - // const start = idx * 3 - // const [cUSDPair, celoPair, celoCUSDPair] = [pairs[start], pairs[start + 1], pairs[start + 2]] - - // // handle cUSD - // if (token.equals(CUSD)) { - // return new Price(CUSD, CUSD, '1', '1') - // } - // let price: Price | undefined = undefined - - // if (celoPair && celoCUSDPair) { - // price = celoPair.priceOf(token).multiply(celoCUSDPair.priceOf(celo)) - // } - - // if (cUSDPair) { - // const newPrice = cUSDPair.priceOf(token) - // price = !price || newPrice.greaterThan(new Fraction(price.numerator, price.denominator)) ? newPrice : price - // } - - // return price - // }) - // return prices.filter((p: Price | undefined) => !p)[0] ?? undefined - // }, [chainId, tokens, CUSD, celo, pairs]) -} diff --git a/tsconfig.json b/tsconfig.json index de4234253ac..f10db7f9e4e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,6 +25,6 @@ "types": ["react-spring", "jest"], "baseUrl": "src" }, - "exclude": ["node_modules"], - "include": ["./src/**/*.ts", "./src/**/*.tsx", "src/components/Confetti/index.js"] + "include": ["./src/**/*.ts", "./src/**/*.tsx", "src/components/Confetti/index.js"], + "exclude": ["node_modules", "./src/generated/**/*.ts"] } diff --git a/yarn.lock b/yarn.lock index 5a9dcfc1658..f79f7fd7591 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2880,13 +2880,13 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^26.0.23": - version "26.0.24" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.24.tgz#943d11976b16739185913a1936e0de0c4a7d595a" - integrity sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w== +"@types/jest@^27.4.1": + version "27.4.1" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.4.1.tgz#185cbe2926eaaf9662d340cc02e548ce9e11ab6d" + integrity sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw== dependencies: - jest-diff "^26.0.0" - pretty-format "^26.0.0" + jest-matcher-utils "^27.0.0" + pretty-format "^27.0.0" "@types/json-schema@*", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8": version "7.0.10" @@ -2913,12 +2913,19 @@ dependencies: "@types/node" "*" -"@types/lodash@^4.14.170", "@types/lodash@^4.14.53": +"@types/lodash.mapvalues@^4.6.6": + version "4.6.6" + resolved "https://registry.yarnpkg.com/@types/lodash.mapvalues/-/lodash.mapvalues-4.6.6.tgz#899b6e1d3b9b4e313bc332ec18182f9fce7aec7c" + integrity sha512-Mt9eg3AqwAt5HShuOu8taiIYg0sLl4w3vDi0++E0VtiOtj9DqQHaxVr3wicVop0eDEqr5ENbht7vsLJlkMHL+w== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*", "@types/lodash@^4.14.170", "@types/lodash@^4.14.53": version "4.14.180" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.180.tgz#4ab7c9ddfc92ec4a887886483bc14c79fb380670" integrity sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g== -"@types/luxon@^2.0.7": +"@types/luxon@^2.0.9": version "2.3.0" resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-2.3.0.tgz#0f4d912c385e47890374cb694da9bc93bacbe2b0" integrity sha512-mWXdRlg+5dWvxU+uaijB2RY5NrJtMEXR6j+D6W66hPuezSVXrQqQvWa/JNHntgEYgjzeoVRrQVmMWAbKjUJiFQ== @@ -3325,6 +3332,17 @@ tiny-warning "^1.0.3" toformat "^2.0.0" +"@ubeswap/token-math@^4.4.4": + version "4.4.4" + resolved "https://registry.yarnpkg.com/@ubeswap/token-math/-/token-math-4.4.4.tgz#98ff131a4371a6229245d8aa79bf198a35682b9c" + integrity sha512-AbcyDhKVPaFQOuzak1UPyBPNbo6IHCKY4C2xq5DOcW6qmlI+4EaxI9mU2jpG/joYAtqPYrx2BbLizU+TfB6bmg== + dependencies: + big.js "^6.1.1" + decimal.js-light "^2.5.1" + tiny-invariant "^1.2.0" + toformat "^2.0.0" + tslib "^2.3.1" + "@uniswap/governance@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@uniswap/governance/-/governance-1.0.2.tgz#7371ab54dea9a5c045275001e2d5325ff2c11a93" @@ -3855,6 +3873,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + ansi-styles@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.1.0.tgz#87313c102b8118abd57371afab34618bf7350ed3" @@ -4445,7 +4468,7 @@ big.js@^5.2.2: resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== -big.js@^6.0.3: +big.js@^6.0.3, big.js@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/big.js/-/big.js-6.1.1.tgz#63b35b19dc9775c94991ee5db7694880655d5537" integrity sha512-1vObw81a8ylZO5ePrtMay0n018TcftpTA5HFKDaSuiUDBo8biRBtjIobw60OpwuvrGk+FsxKamqN4cnmj/eXdg== @@ -6288,6 +6311,11 @@ diff-sequences@^26.6.2: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== +diff-sequences@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" + integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== + diff@3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" @@ -9293,7 +9321,7 @@ jest-config@^26.6.3: micromatch "^4.0.2" pretty-format "^26.6.2" -jest-diff@^26.0.0, jest-diff@^26.6.2: +jest-diff@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== @@ -9303,6 +9331,16 @@ jest-diff@^26.0.0, jest-diff@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" +jest-diff@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" + integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== + dependencies: + chalk "^4.0.0" + diff-sequences "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + jest-docblock@^26.0.0: version "26.0.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" @@ -9351,6 +9389,11 @@ jest-get-type@^26.3.0: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== +jest-get-type@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" + integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== + jest-haste-map@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" @@ -9414,6 +9457,16 @@ jest-matcher-utils@^26.6.0, jest-matcher-utils@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" +jest-matcher-utils@^27.0.0: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" + integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== + dependencies: + chalk "^4.0.0" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + jest-message-util@^26.6.0, jest-message-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" @@ -9699,6 +9752,11 @@ jsbi@^3.1.4: resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-3.2.5.tgz#b37bb90e0e5c2814c1c2a1bcd8c729888a2e37d6" integrity sha512-aBE4n43IPvjaddScbvWRA2YlTzKEynHzu7MqOyTipdHucf/VxS63ViCjxYRg86M8Rxwbt/GfzHl1kKERkt45fQ== +jsbi@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-4.2.0.tgz#975567e0dd47fdf91bad4b5a681070d15e244b68" + integrity sha512-JzhwPkH7Ra8O1b5uHmWxl6N8ZumqGvMXgpKcsq0/iWlnB+KkzbekCIcLl3hu3sFGTLNXAuXIqY4STtE0ZfT1zA== + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -10119,6 +10177,11 @@ lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= +lodash.mapvalues@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c" + integrity sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw= + lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -12395,7 +12458,7 @@ pretty-error@^2.1.1: lodash "^4.17.20" renderkid "^2.0.4" -pretty-format@^26.0.0, pretty-format@^26.6.0, pretty-format@^26.6.2: +pretty-format@^26.6.0, pretty-format@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== @@ -12405,6 +12468,15 @@ pretty-format@^26.0.0, pretty-format@^26.6.0, pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" +pretty-format@^27.0.0, pretty-format@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -14864,7 +14936,7 @@ timsort@^0.3.0: resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= -tiny-invariant@^1.0.2, tiny-invariant@^1.1.0: +tiny-invariant@^1.0.2, tiny-invariant@^1.1.0, tiny-invariant@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9" integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg== @@ -15068,7 +15140,7 @@ tslib@^1.0.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0: +tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==