diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx
index 639c7d1ba..7d523768b 100644
--- a/src/components/Button/index.tsx
+++ b/src/components/Button/index.tsx
@@ -29,6 +29,7 @@ const OUTLINED = {
green: 'bg-green bg-opacity-20 border border-green rounded text-green hover:bg-opacity-40 disabled:bg-opacity-20',
gradient:
'border border-transparent border-gradient-r-blue-pink-dark-900 opacity-80 hover:opacity-100 disabled:bg-opacity-20',
+ border: 'border border-[#546D2A] bg-blue bg-opacity-20 outline-blue rounded text-primary hover:bg-opacity-40 disabled:bg-opacity-20',
}
const EMPTY = {
@@ -48,7 +49,7 @@ const VARIANT = {
link: LINK,
}
-export type ButtonColor = 'blue' | 'pink' | 'gradient' | 'gray' | 'default' | 'red' | 'green'
+export type ButtonColor = 'blue' | 'pink' | 'gradient' | 'gray' | 'default' | 'red' | 'green' | 'border'
export type ButtonSize = 'xs' | 'sm' | 'lg' | 'default' | 'none'
diff --git a/src/components/PanelLimitPrice/index.tsx b/src/components/PanelLimitPrice/index.tsx
new file mode 100755
index 000000000..4232d6bbf
--- /dev/null
+++ b/src/components/PanelLimitPrice/index.tsx
@@ -0,0 +1,58 @@
+import React, { useState } from 'react'
+import { classNames, escapeRegExp } from '../../functions'
+import Input from '../Input'
+
+
+const PanelLimitPrice = ({label, currencyA, currencyB}) => {
+ const [value, setValue] = useState('')
+
+ const handleValue = (e) => {
+ setValue(e.target.value)
+ }
+
+ const inputRegex = RegExp(`^\\d*(?:\\\\[.])?\\d*$`) // match escaped "." characters via in a non-capturing group
+
+ const defaultClassName = 'w-0 p-0 text-2xl bg-transparent'
+
+ const enforcer = (nextUserInput: string) => {
+ if (nextUserInput === '' || inputRegex.test(escapeRegExp(nextUserInput))) {
+ setValue(nextUserInput)
+ }
+ }
+
+ return (
+
+
+ {label}
+
+
{
+ // replace commas with periods, because uniswap exclusively uses period as the decimal separator
+ enforcer(event.target.value.replace(/,/g, '.'))
+ }}
+ // universal input options
+ inputMode="decimal"
+ title="Token Amount"
+ autoComplete="off"
+ autoCorrect="off"
+ // text-specific options
+ type="text"
+ pattern="^[0-9]*[.,]?[0-9]*$"
+ placeholder={'0.0'}
+ min={0.0}
+ minLength={1}
+ maxLength={14}
+ spellCheck="false"
+ className={'relative flex text-center justify-center font-bold outline-none border-none overflow-hidden overflow-ellipsis placeholder-low-emphesis focus:placeholder-primary text-xl bg-transparent'}
+ // readOnly={true}
+ />
+
+ {/* {label} */}
+ {currencyA} per {currencyB}
+
+
+ )
+}
+
+export default PanelLimitPrice
\ No newline at end of file
diff --git a/src/features/onsen/FarmMenu.tsx b/src/features/onsen/FarmMenu.tsx
index e78406caa..f9d757cd2 100644
--- a/src/features/onsen/FarmMenu.tsx
+++ b/src/features/onsen/FarmMenu.tsx
@@ -6,7 +6,31 @@ import { useLingui } from '@lingui/react'
import { useActiveWeb3React } from '../../hooks'
import { useWalletModalToggle } from '../../state/application/hooks'
-const Menu = ({ positionsLength }) => {
+const defaultOptions = [
+ {
+ href: `/farm?filter=portfolio`,
+ label: 'Your Farms',
+ exact: true
+ },
+ {
+ divider: true
+ },
+ {
+ href: "/farm?filter=all",
+ label: 'All Farms'
+ },
+ {
+ href: `/farm?filter=2x`,
+ label: '2x Reward Farms',
+ exact: true
+ },
+ {
+ href: "/farm?filter=past",
+ label: 'Past Farms'
+ }
+]
+
+const Menu = ({ positionsLength, options = defaultOptions}) => {
const { account, chainId } = useActiveWeb3React()
const { i18n } = useLingui()
const toggleWalletModal = useWalletModalToggle()
@@ -14,15 +38,22 @@ const Menu = ({ positionsLength }) => {
return (
)
}
export default Menu
+
+
+
diff --git a/src/features/robots/RobotList.tsx b/src/features/robots/RobotList.tsx
new file mode 100644
index 000000000..5e697afeb
--- /dev/null
+++ b/src/features/robots/RobotList.tsx
@@ -0,0 +1,74 @@
+import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/outline'
+import InfiniteScroll from 'react-infinite-scroll-component'
+import Dots from '../../components/Dots'
+import React from 'react'
+import { t } from '@lingui/macro'
+import { useLingui } from '@lingui/react'
+import useSortableData from '../../hooks/useSortableData'
+import { useInfiniteScroll } from '../onsen/hooks'
+import RobotListItems from './RobotListItems'
+
+const RobotList = ({ robots, term }) => {
+ const { items, requestSort, sortConfig } = useSortableData(robots)
+ const { i18n } = useLingui()
+ const [numDisplayed, setNumDisplayed] = useInfiniteScroll(items)
+
+ return items ? (
+ <>
+
+
requestSort('symbol')}
+ >
+
{i18n._(t`Pair`)}
+ {sortConfig &&
+ sortConfig.key === 'symbol' &&
+ ((sortConfig.direction === 'ascending' &&
) ||
+ (sortConfig.direction === 'descending' &&
))}
+
+
requestSort('tvl')}
+ >
+ {i18n._(t`Min Price to Buy`)}
+ {sortConfig &&
+ sortConfig.key === 'tvl' &&
+ ((sortConfig.direction === 'ascending' && ) ||
+ (sortConfig.direction === 'descending' && ))}
+
+
requestSort('roiPerYear')}
+ >
+ {i18n._(t`Max Price to Sell`)}
+ {sortConfig &&
+ sortConfig.key === 'roiPerYear' &&
+ ((sortConfig.direction === 'ascending' && ) ||
+ (sortConfig.direction === 'descending' && ))}
+
+
+ {i18n._(t`Actions`)}
+
+
+ setNumDisplayed(numDisplayed + 5)}
+ hasMore={true}
+ loader={null}
+ >
+
+ {items.slice(0, numDisplayed).map((robot, index) => (
+
+ ))}
+
+
+ >
+ ) : (
+ {term ? No Results. : Loading}
+ )
+}
+
+export default RobotList
diff --git a/src/features/robots/RobotListItemDetails.tsx b/src/features/robots/RobotListItemDetails.tsx
new file mode 100644
index 000000000..005fbb3fb
--- /dev/null
+++ b/src/features/robots/RobotListItemDetails.tsx
@@ -0,0 +1,145 @@
+import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
+import { ChainId, CurrencyAmount, JSBI, MASTERCHEF_ADDRESS, MASTERCHEF_V2_ADDRESS, Token, ZERO } from '@tangoswapcash/sdk'
+import { Disclosure, Transition } from '@headlessui/react'
+import React, { useState } from 'react'
+
+import Button, { ButtonError } from '../../components/Button'
+import Dots from '../../components/Dots'
+import Input from '../../components/Input'
+import { formatCurrencyAmount, formatNumber, formatPercent } from '../../functions'
+import { getAddress } from '@ethersproject/address'
+import { t } from '@lingui/macro'
+import { tryParseAmount } from '../../functions/parse'
+import useActiveWeb3React from '../../hooks/useActiveWeb3React'
+import { useLingui } from '@lingui/react'
+import { useTokenBalance } from '../../state/wallet/hooks'
+import { useTransactionAdder } from '../../state/transactions/hooks'
+import { BigNumber } from '@ethersproject/bignumber'
+import { isMobile } from 'react-device-detect'
+import { useRouter } from 'next/router'
+import { Chef, PairType } from '../onsen/enum'
+import { usePendingSushi, useUserInfo } from '../onsen/hooks'
+import useMasterChef from '../onsen/useMasterChef'
+import usePendingReward from '../onsen/usePendingReward'
+
+const RobotListItemDetails = ({ robot }) => {
+ const { i18n } = useLingui()
+
+ const router = useRouter()
+
+ const { account, chainId } = useActiveWeb3React()
+ const [pendingTx, setPendingTx] = useState(false)
+ const [maxValue, setMaxValue] = useState(robot.maxValue ? robot.maxValue : 0)
+ const [minValue, setMinValue] = useState(robot.minValue ? robot.minValue : 0)
+
+ const addTransaction = useTransactionAdder()
+
+ const liquidityToken = new Token(
+ chainId,
+ getAddress(robot.pair.id),
+ robot.pair.type === PairType.KASHI ? Number(robot.pair.asset.decimals) : 18,
+ robot.pair.symbol,
+ robot.pair.name
+ )
+
+ // User liquidity token balance
+ const balance = useTokenBalance(account, liquidityToken)
+
+ // TODO: Replace these
+ const amount = useUserInfo(robot, liquidityToken)
+
+ const handleSellRobot = () => {
+ console.log('Vendido')
+ }
+
+ const handleBuyRobot = () => {
+ console.log('Borrado')
+ }
+
+ const handleDeleteRobot = () => {
+ console.log('Borrado')
+ }
+
+ const { deposit, withdraw, harvest } = useMasterChef(robot.chef)
+
+ const poolFraction = (Number.parseFloat(amount?.toFixed()) / robot.chefBalance) || 0
+ const token0Reserve = robot.pool.reserves ? (robot.pool.reserves.reserve0 as BigNumber).toString() : 0
+ const token0Amount = CurrencyAmount.fromRawAmount(robot.pair.token0, JSBI.BigInt(token0Reserve)).multiply(Math.round(poolFraction * 1e8)).divide(1e8)
+ const token1Reserve = robot.pool.reserves ? (robot.pool.reserves.reserve1 as BigNumber).toString() : 0
+ const token1Amount = CurrencyAmount.fromRawAmount(robot.pair.token1, JSBI.BigInt(token1Reserve)).multiply(Math.round(poolFraction * 1e8)).divide(1e8)
+ const token0Name = robot.pool.token0 === robot.pair.token0.id ? robot.pair.token0.symbol : robot.pair.token1.symbol
+ const token1Name = robot.pool.token1 === robot.pair.token1.id ? robot.pair.token1.symbol : robot.pair.token0.symbol
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ setMaxValue(value)
+ }}
+ disabled
+ />
+
+
+
+ { robot.pending !== 0 ? (
+
+
+
+
+
+
+
+
+ ) : (
+
+
+
+ )
+ }
+
+
+ )
+}
+
+export default RobotListItemDetails
diff --git a/src/features/robots/RobotListItems.tsx b/src/features/robots/RobotListItems.tsx
new file mode 100644
index 000000000..b5545dfde
--- /dev/null
+++ b/src/features/robots/RobotListItems.tsx
@@ -0,0 +1,99 @@
+import { classNames, formatNumber, formatPercent } from '../../functions'
+import { DotsHorizontalIcon } from '@heroicons/react/outline'
+import { ZERO } from '@tangoswapcash/sdk'
+import { Disclosure } from '@headlessui/react'
+import DoubleLogo from '../../components/DoubleLogo'
+import Image from '../../components/Image'
+import React from 'react'
+import { useLingui } from '@lingui/react'
+import { t } from '@lingui/macro'
+import { useCurrency } from '../../hooks/Tokens'
+import { isMobile } from 'react-device-detect'
+import { usePendingSushi } from '../onsen/hooks'
+import usePendingReward from '../onsen/usePendingReward'
+import RobotListItemDetails from './RobotListItemDetails'
+import { PairType } from '../onsen/enum'
+
+const RobotListItems = ({ robot, ...rest }) => {
+ const token0 = useCurrency(robot.pair.token0.id)
+ const token1 = useCurrency(robot.pair.token1.id)
+
+ const pendingSushi = usePendingSushi(robot)
+ const rewardAmount = usePendingReward(robot)
+
+ const { i18n } = useLingui()
+
+ return (
+
+ {({ open }) => (
+ <>
+
+
+
+
+
+
+
{robot?.pair?.token0?.symbol}
+
+ {robot?.pair?.token1?.symbol}
+
+
+
+
+
+ $ {robot.allocPoint / 4}
+
+
+
+ $ {robot.allocPoint}
+
+
+ {pendingSushi && pendingSushi.greaterThan(ZERO) ? (
+
+
+
+ {robot?.rewards?.map((reward, i) => (
+
+
+
+ ))}
+
+
+
+ {robot?.rewards?.map((reward, i) => (
+
+ {i == 0 ? formatNumber(pendingSushi.toFixed(18)) : formatNumber(rewardAmount)} {reward.token}
+
+ ))}
+
+
+ ) : (
+
+ {i18n._(t`Stake LP to robot`)}
+ {/* */}
+
+ )}
+
+
+ {/* {open && } */}
+ >
+ )}
+
+ )
+}
+
+export default RobotListItems
+
+// robot.rewards.length > 1 ? `& ${formatNumber(reward)} ${robot.rewards[1].token}` : ''
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index 301442ca6..07c9a74df 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -2,9 +2,11 @@ import Container from '../components/Container'
import Head from 'next/head'
// import Swap from './exchange/smart-swap/[[...tokens]]'
import LimitOrder from './exchange/limit-order/[[...tokens]]'
+import CreateRobotPage from './robots/create-robot'
+import RobotList from './robots/robots-list'
export default function Dashboard() {
- return LimitOrder()
+ return RobotList()
// return (
//
//
diff --git a/src/pages/robots/create-robot/index.tsx b/src/pages/robots/create-robot/index.tsx
new file mode 100755
index 000000000..1de7403c6
--- /dev/null
+++ b/src/pages/robots/create-robot/index.tsx
@@ -0,0 +1,358 @@
+import { ApprovalState, useApproveCallback } from '../../../hooks/useApproveCallback'
+import { AutoRow, RowBetween } from '../../../components/Row'
+import Button, { ButtonError } from '../../../components/Button'
+import { Currency, CurrencyAmount, Percent, WNATIVE, currencyEquals } from '@tangoswapcash/sdk'
+import { ONE_BIPS, ZERO_PERCENT } from '../../../constants'
+import React, { useCallback, useState } from 'react'
+import TransactionConfirmationModal, { ConfirmationModalContent } from '../../../modals/TransactionConfirmationModal'
+import { calculateGasMargin, calculateSlippageAmount, getGasPrice } from '../../../functions/trade'
+import { currencyId, maxAmountSpend } from '../../../functions/currency'
+import { useDerivedMintInfo, useMintActionHandlers, useMintState } from '../../../state/mint/hooks'
+import { useExpertModeManager, useUserSlippageToleranceWithDefault } from '../../../state/user/hooks'
+
+import Alert from '../../../components/Alert'
+import { AutoColumn } from '../../../components/Column'
+import { BigNumber } from '@ethersproject/bignumber'
+import { ConfirmAddModalBottom } from '../../../features/exchange-v1/liquidity/ConfirmAddModalBottom'
+import Container from '../../../components/Container'
+import CurrencyInputPanel from '../../../components/CurrencyInputPanel'
+import CurrencyLogo from '../../../components/CurrencyLogo'
+import Dots from '../../../components/Dots'
+import DoubleCurrencyLogo from '../../../components/DoubleLogo'
+import DoubleGlowShadow from '../../../components/DoubleGlowShadow'
+import ExchangeHeader from '../../../features/trade/Header'
+import { Field } from '../../../state/mint/actions'
+import Head from 'next/head'
+import LiquidityHeader from '../../../features/exchange-v1/liquidity/LiquidityHeader'
+import LiquidityPrice from '../../../features/exchange-v1/liquidity/LiquidityPrice'
+import { MinimalPositionCard } from '../../../components/PositionCard'
+import NavLink from '../../../components/NavLink'
+import { PairState } from '../../../hooks/useV2Pairs'
+import { Plus } from 'react-feather'
+import { TransactionResponse } from '@ethersproject/providers'
+import Typography from '../../../components/Typography'
+import UnsupportedCurrencyFooter from '../../../features/exchange-v1/swap/UnsupportedCurrencyFooter'
+import Web3Connect from '../../../components/Web3Connect'
+import { t } from '@lingui/macro'
+import { useActiveWeb3React } from '../../../hooks/useActiveWeb3React'
+import { useCurrency } from '../../../hooks/Tokens'
+import { useIsSwapUnsupported } from '../../../hooks/useIsSwapUnsupported'
+import { useLingui } from '@lingui/react'
+import { useRouter } from 'next/router'
+import { useRouterContract } from '../../../hooks'
+import { useTransactionAdder } from '../../../state/transactions/hooks'
+import useTransactionDeadline from '../../../hooks/useTransactionDeadline'
+import { useWalletModalToggle } from '../../../state/application/hooks'
+import PanelLimitPrice from '../../../components/PanelLimitPrice'
+
+const DEFAULT_ADD_V2_SLIPPAGE_TOLERANCE = new Percent(50, 10_000)
+
+export default function CreateRobotPage() {
+ const { i18n } = useLingui()
+ const { account, chainId, library } = useActiveWeb3React()
+ const router = useRouter()
+ const tokens = router.query.tokens
+ const [currencyIdA, currencyIdB] = (tokens as string[]) || [undefined, undefined]
+
+ const currencyA = useCurrency(currencyIdA)
+ const currencyB = useCurrency(currencyIdB)
+
+ const [currenciesSelected, setCurrenciesSelected] = useState(null)
+
+ const oneCurrencyIsWETH = Boolean(
+ chainId &&
+ ((currencyA && currencyEquals(currencyA, WNATIVE[chainId])) ||
+ (currencyB && currencyEquals(currencyB, WNATIVE[chainId])))
+ )
+
+ const toggleWalletModal = useWalletModalToggle() // toggle wallet when disconnected
+
+ const [isExpertMode] = useExpertModeManager()
+
+ // mint state
+ const { independentField, typedValue, otherTypedValue } = useMintState()
+ const {
+ dependentField,
+ currencies,
+ pair,
+ pairState,
+ currencyBalances,
+ parsedAmounts,
+ price,
+ noLiquidity,
+ liquidityMinted,
+ poolTokenPercentage,
+ error,
+ } = useDerivedMintInfo(currencyA ?? undefined, currencyB ?? undefined)
+
+ const { onFieldAInput, onFieldBInput } = useMintActionHandlers(noLiquidity)
+
+ const isValid = !error
+
+ // modal and loading
+ const [showConfirm, setShowConfirm] = useState(false)
+ const [attemptingTxn, setAttemptingTxn] = useState(false) // clicked confirm
+
+ // txn values
+ const deadline = useTransactionDeadline() // custom from users settings
+
+ // const [allowedSlippage] = useUserSlippageTolerance(); // custom from users
+
+ const allowedSlippage = useUserSlippageToleranceWithDefault(DEFAULT_ADD_V2_SLIPPAGE_TOLERANCE) // custom from users
+
+ const [txHash, setTxHash] = useState('')
+
+ // get formatted amounts
+ const formattedAmounts = {
+ [independentField]: typedValue,
+ [dependentField]: noLiquidity ? otherTypedValue : parsedAmounts[dependentField]?.toSignificant(6) ?? '',
+ }
+
+ // get the max amounts user can add
+ const maxAmounts: { [field in Field]?: CurrencyAmount } = [Field.CURRENCY_A, Field.CURRENCY_B].reduce(
+ (accumulator, field) => {
+ return {
+ ...accumulator,
+ [field]: maxAmountSpend(currencyBalances[field]),
+ }
+ },
+ {}
+ )
+
+ const atMaxAmounts: { [field in Field]?: CurrencyAmount } = [Field.CURRENCY_A, Field.CURRENCY_B].reduce(
+ (accumulator, field) => {
+ return {
+ ...accumulator,
+ [field]: maxAmounts[field]?.equalTo(parsedAmounts[field] ?? '0'),
+ }
+ },
+ {}
+ )
+
+ const routerContract = useRouterContract()
+
+ // check whether the user has approved the router on the tokens
+ const [approvalA, approveACallback] = useApproveCallback(parsedAmounts[Field.CURRENCY_A], routerContract?.address)
+ const [approvalB, approveBCallback] = useApproveCallback(parsedAmounts[Field.CURRENCY_B], routerContract?.address)
+
+ const addTransaction = useTransactionAdder()
+
+ async function onAdd() {
+ if (!chainId || !library || !account || !routerContract) return
+
+ const { [Field.CURRENCY_A]: parsedAmountA, [Field.CURRENCY_B]: parsedAmountB } = parsedAmounts
+
+ console.log({ parsedAmountA, parsedAmountB, currencyA, currencyB, deadline })
+
+ if (!parsedAmountA || !parsedAmountB || !currencyA || !currencyB || !deadline) {
+ return
+ }
+
+ const amountsMin = {
+ [Field.CURRENCY_A]: calculateSlippageAmount(parsedAmountA, noLiquidity ? ZERO_PERCENT : allowedSlippage)[0],
+ [Field.CURRENCY_B]: calculateSlippageAmount(parsedAmountB, noLiquidity ? ZERO_PERCENT : allowedSlippage)[0],
+ }
+
+ let estimate,
+ method: (...args: any) => Promise,
+ args: Array,
+ value: BigNumber | null
+ if (currencyA.isNative || currencyB.isNative) {
+ const tokenBIsETH = currencyB.isNative
+ estimate = routerContract.estimateGas.addLiquidityETH
+ method = routerContract.addLiquidityETH
+ args = [
+ (tokenBIsETH ? currencyA : currencyB)?.wrapped?.address ?? '', // token
+ (tokenBIsETH ? parsedAmountA : parsedAmountB).quotient.toString(), // token desired
+ amountsMin[tokenBIsETH ? Field.CURRENCY_A : Field.CURRENCY_B].toString(), // token min
+ amountsMin[tokenBIsETH ? Field.CURRENCY_B : Field.CURRENCY_A].toString(), // eth min
+ account,
+ deadline.toHexString(),
+ ]
+ value = BigNumber.from((tokenBIsETH ? parsedAmountB : parsedAmountA).quotient.toString())
+ } else {
+ estimate = routerContract.estimateGas.addLiquidity
+ method = routerContract.addLiquidity
+ args = [
+ currencyA?.wrapped?.address ?? '',
+ currencyB?.wrapped?.address ?? '',
+ parsedAmountA.quotient.toString(),
+ parsedAmountB.quotient.toString(),
+ amountsMin[Field.CURRENCY_A].toString(),
+ amountsMin[Field.CURRENCY_B].toString(),
+ account,
+ deadline.toHexString(),
+ ]
+ value = null
+ }
+
+ setAttemptingTxn(true)
+ try {
+ const estimatedGasLimit = await estimate(...args, {
+ ...(value ? { value } : {}),
+ gasPrice: getGasPrice(),
+ })
+
+ const response = await method(...args, {
+ ...(value ? { value } : {}),
+ gasLimit: calculateGasMargin(estimatedGasLimit),
+ gasPrice: getGasPrice(),
+ })
+
+ setAttemptingTxn(false)
+
+ addTransaction(response, {
+ summary: i18n._(
+ t`Add ${parsedAmounts[Field.CURRENCY_A]?.toSignificant(3)} ${
+ currencies[Field.CURRENCY_A]?.symbol
+ } and ${parsedAmounts[Field.CURRENCY_B]?.toSignificant(3)} ${currencies[Field.CURRENCY_B]?.symbol}`
+ ),
+ })
+
+ setTxHash(response.hash)
+ } catch (error) {
+ setAttemptingTxn(false)
+ // we only care if the error is something _other_ than the user rejected the tx
+ if (error?.code !== 4001) {
+ console.error(error)
+ }
+ }
+ }
+
+ const handleCurrencyASelect = (currencyA: Currency) => {
+ setCurrenciesSelected({...currenciesSelected, currencyA: currencyA})
+ }
+ const handleCurrencyBSelect = (currencyB: Currency) => {
+ console.log(currenciesSelected)
+ setCurrenciesSelected({...currenciesSelected, currencyB: currencyB})
+ }
+
+ const addIsUnsupported = useIsSwapUnsupported(currencies?.CURRENCY_A, currencies?.CURRENCY_B)
+
+ // console.log(
+ // { addIsUnsupported, isValid, approvaonFieldBInputlA, approvalB },
+ // approvalA === ApprovalState.APPROVED && approvalB === ApprovalState.APPROVED
+ // )
+ return (
+ <>
+
+ Create Robots | Orders.Cash
+
+
+
+
+
+
+
+
+
+ {pair && pairState !== PairState.INVALID && (
+
+ )}
+
+
{
+ onFieldAInput(maxAmounts[Field.CURRENCY_A]?.toExact() ?? '')
+ }}
+ onCurrencySelect={handleCurrencyASelect}
+ showMaxButton={!atMaxAmounts[Field.CURRENCY_A]}
+ currency={currenciesSelected && currenciesSelected.currencyA && currenciesSelected.currencyA}
+ id="add-liquidity-input-tokena"
+ showCommonBases
+ />
+
+
+
+
+
+
+
+ {
+ onFieldBInput(maxAmounts[Field.CURRENCY_B]?.toExact() ?? '')
+ }}
+ showMaxButton={!atMaxAmounts[Field.CURRENCY_B]}
+ currency={currenciesSelected && currenciesSelected.currencyB && currenciesSelected.currencyB}
+ id="add-liquidity-input-tokenb"
+ showCommonBases
+ />
+
+
+ {currencies[Field.CURRENCY_A] && currencies[Field.CURRENCY_B] && pairState !== PairState.INVALID && (
+
+
+
+ )}
+
+ {
+ currenciesSelected && currenciesSelected?.currencyA && currenciesSelected?.currencyB && (
+
+ )
+ }
+
+ {addIsUnsupported ? (
+
+ ) : !account ? (
+
+ ) : (
+
+ )}
+
+
+ {!addIsUnsupported ? (
+ pair && !noLiquidity && pairState !== PairState.INVALID ? (
+
+ ) : null
+ ) : (
+
+ )}
+
+
+
+ >
+ )
+}
diff --git a/src/pages/robots/robots-list/index.tsx b/src/pages/robots/robots-list/index.tsx
new file mode 100644
index 000000000..f8c1ff87d
--- /dev/null
+++ b/src/pages/robots/robots-list/index.tsx
@@ -0,0 +1,859 @@
+import { Chef, PairType } from '../../../features/onsen/enum'
+import { PlusIcon } from '@heroicons/react/outline'
+import { useActiveWeb3React, useFuse } from '../../../hooks'
+import {
+ useAverageBlockTime,
+ useEthPrice,
+ useFarmPairAddresses,
+ useFarms,
+ useMasterChefV1SushiPerBlock,
+ useMasterChefV1TotalAllocPoint,
+ useSushiPairs,
+ useSushiPrice,
+} from '../../../services/graph'
+
+import { BigNumber } from '@ethersproject/bignumber'
+import {
+ ChainId,
+ WNATIVE,
+ Token,
+ CurrencyAmount,
+ JSBI,
+ WBCH,
+ MASTERCHEF_ADDRESS,
+ MASTERCHEF_V2_ADDRESS,
+} from '@tangoswapcash/sdk'
+import { TANGO, FLEXUSD } from '../../../config/tokens'
+import Container from '../../../components/Container'
+import FarmList from '../../../features/onsen/FarmList'
+import Head from 'next/head'
+import Menu from '../../../features/onsen/FarmMenu'
+import React, { useEffect } from 'react'
+import Search from '../../../components/Search'
+import { classNames } from '../../../functions'
+import dynamic from 'next/dynamic'
+import { getAddress } from '@ethersproject/address'
+import useFarmRewards from '../../../hooks/useFarmRewards'
+import usePool from '../../../hooks/usePool'
+import { useTokenBalancesWithLoadingIndicator } from '../../../state/wallet/hooks'
+import { usePositions, usePendingSushi } from '../../../features/onsen/hooks'
+import { useRouter } from 'next/router'
+import { updateUserFarmFilter } from '../../../state/user/actions'
+import { getFarmFilter, useUpdateFarmFilter } from '../../../state/user/hooks'
+import { t } from '@lingui/macro'
+import { useLingui } from '@lingui/react'
+import RobotList from '../../../features/robots/RobotList'
+import Button from '../../../components/Button'
+import NavLink from '../../../components/NavLink'
+
+function getTokensSorted(pool, pair) {
+ if (pool.token0 == pair.token0.address && pool.token1 == pair.token1.address) {
+ return [pair.token0, pair.token1, pool.reserves[0], pool.reserves[1]]
+ }
+
+ if (pool.token0 == pair.token1.address && pool.token1 == pair.token0.address) {
+ return [pair.token0, pair.token1, pool.reserves[1], pool.reserves[0]]
+ }
+
+ return [undefined, undefined, undefined, undefined]
+}
+
+function getTokenPriceInBch(pool, pair, chainId, tangoPriceBCH, bchPriceUSD) {
+ let [token0, token1, reserve0, reserve1] = getTokensSorted(pool, pair)
+
+ if (!token0) return 0
+
+ let factor = 0
+ let tokenAmount0 = Number.parseFloat(CurrencyAmount.fromRawAmount(token0, JSBI.BigInt(reserve0.toString())).toFixed())
+ let tokenAmount1 = Number.parseFloat(CurrencyAmount.fromRawAmount(token1, JSBI.BigInt(reserve1.toString())).toFixed())
+
+ if (token0.address === TANGO[chainId].address) {
+ factor = tangoPriceBCH
+ } else if (token1.address === TANGO[chainId].address) {
+ ;[tokenAmount1, tokenAmount0] = [tokenAmount0, tokenAmount1]
+ factor = tangoPriceBCH
+ } else if (token0.address === FLEXUSD.address) {
+ factor = bchPriceUSD
+ } else if (token1.address === FLEXUSD.address) {
+ ;[tokenAmount1, tokenAmount0] = [tokenAmount0, tokenAmount1]
+ factor = bchPriceUSD
+ } else if (token0.address === WBCH[chainId].address) {
+ factor = 1
+ } else if (token1.address === WBCH[chainId].address) {
+ ;[tokenAmount1, tokenAmount0] = [tokenAmount0, tokenAmount1]
+ factor = 1
+ }
+ const derivedETH = (tokenAmount0 / tokenAmount1) * factor
+ return derivedETH
+}
+
+export default function Robot(): JSX.Element {
+ const { i18n } = useLingui()
+ const { chainId } = useActiveWeb3React()
+ const router = useRouter()
+
+ const type = router.query.filter as string
+
+ const savedFilter = getFarmFilter()
+
+ const updateFarmFilter = useUpdateFarmFilter()
+ updateFarmFilter(type)
+
+ const hardcodedPairs = {
+ [ChainId.SMARTBCH]: {
+ '0x7963269e8a854D3F9D98a6cDEa81efFb31B4D1f2': {
+ farmId: 0,
+ allocPoint: 9999999,
+ token0: TANGO[ChainId.SMARTBCH],
+ token1: new Token(ChainId.SMARTBCH, '0x98Ff640323C059d8C4CB846976973FEEB0E068aA', 18, 'XTANGO', 'TANGObar'),
+ },
+ '0xf8534BB9603c501Bbe16deF7D08D941F0241855b': {
+ farmId: 1,
+ allocPoint: 205699999,
+ token0: TANGO[ChainId.SMARTBCH],
+ token1: FLEXUSD,
+ },
+ '0x4b773a2ea30C6A77564E4FaE60204e7Bc0a81A90': {
+ farmId: 2,
+ allocPoint: 509999999,
+ token0: TANGO[ChainId.SMARTBCH],
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0xA15F8102AB4723A4D1554363c0c8AFF471F16E21': {
+ farmId: 3,
+ allocPoint: 170250512,
+ token0: FLEXUSD,
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0x1A2bdFF5bA942bF20f0db7218cdE28D19aC8dD20': {
+ farmId: 4,
+ allocPoint: 14999999,
+ token0: new Token(ChainId.SMARTBCH, '0x98Dd7eC28FB43b3C4c770AE532417015fa939Dd3', 18, 'FLEX', 'FLEX Coin'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0x5b860757a77c62Dca833542e8E4650AEE777a08F': {
+ farmId: 5,
+ allocPoint: 0,
+ token0: new Token(ChainId.SMARTBCH, '0x5fA664f69c2A4A3ec94FaC3cBf7049BD9CA73129', 18, 'MIST', 'MistToken'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0xd12C1De8740406438eb84Dde44cd0839F48211aa': {
+ farmId: 6,
+ allocPoint: 0,
+ token0: new Token(ChainId.SMARTBCH, '0x77CB87b57F54667978Eb1B199b28a0db8C8E1c0B', 18, 'EBEN', 'Green Ben'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0xa790208A8C49e586a3F2145aD2c9096d6072E1F3': {
+ farmId: 7,
+ allocPoint: 0,
+ token0: new Token(ChainId.SMARTBCH, '0xc8E09AEdB3c949a875e1FD571dC4b3E48FB221f0', 18, 'MILK', 'Milk'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0xF463db65674426A58E9C3fE557FaaE338026ef39': {
+ farmId: 8,
+ allocPoint: 0,
+ token0: new Token(
+ ChainId.SMARTBCH,
+ '0x675E1d6FcE8C7cC091aED06A68D079489450338a',
+ 18,
+ 'ARG',
+ 'Bitcoin Cash Argentina'
+ ),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0xCFa5B1C5FaBF867842Ac3C25E729Fc3671d27c50': {
+ farmId: 9,
+ allocPoint: 0,
+ token0: new Token(ChainId.SMARTBCH, '0xc70c7718C7f1CCd906534C2c4a76914173EC2c44', 18, 'KTH', 'Knuth'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0xf9185C281fE4C8af452244A65cE7317345352942': {
+ farmId: 10,
+ allocPoint: 1250000,
+ token0: new Token(ChainId.SMARTBCH, '0xe11829a7d5d8806bb36e118461a1012588fafd89', 18, 'SPICE', 'SPICE'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0xBe0e246a87a3e9a2D2Db2efD384E0174F13E62b1': {
+ farmId: 11,
+ allocPoint: 4999999,
+ token0: new Token(ChainId.SMARTBCH, '0x0b00366fBF7037E9d75E4A569ab27dAB84759302', 18, 'LAW', 'LAWTOKEN'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0x1946978E39E6105fEb2107D9c01197a962746bf5': {
+ farmId: 12,
+ allocPoint: 499999,
+ token0: new Token(ChainId.SMARTBCH, '0xff3ed63bf8bc9303ea0a7e1215ba2f82d569799e', 18, 'ORB', 'ORB'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0x23e1E177aE511342fFc27F59da57685b3a0413bc': {
+ farmId: 13,
+ allocPoint: 4999999,
+ token0: new Token(ChainId.SMARTBCH, '0x265bD28d79400D55a1665707Fa14A72978FA6043', 2, 'CATS', 'CashCats'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0x5340619781a8963377aFD76A6999edB6437e3B72': {
+ farmId: 14,
+ allocPoint: 2499999,
+ token0: new Token(ChainId.SMARTBCH, '0x6732E55Ac3ECa734F54C26Bd8DF4eED52Fb79a6E', 2, 'JOY', 'Joystick.club'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0x91dde68D0C08e8620d77B8e7F1836aD4ec3CB33c': {
+ farmId: 15,
+ allocPoint: 2499999,
+ token0: new Token(ChainId.SMARTBCH, '0x7642Df81b5BEAeEb331cc5A104bd13Ba68c34B91', 2, 'CLY', 'Celery'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0xa24e2a9a41020bD1EaD472aF07bCc74cd7fB85A4': {
+ farmId: 16,
+ allocPoint: 499999,
+ token0: new Token(ChainId.SMARTBCH, '0xca0235058985fcc1839e9e37c10900a73c126708', 7, 'DAO', 'DAO'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0x0152F077D2808506FbF6B991b48D1e8DDCBF7107': {
+ farmId: 17,
+ allocPoint: 499999,
+ token0: new Token(ChainId.SMARTBCH, '0x3d13DaFcCA3a188DB340c81414239Bc2be312Ec9', 18, 'AXIEBCH', 'AxieBCH'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0x9E59AAc21DaB7C89d0BDA99335386868539Af9B8': {
+ farmId: 18,
+ allocPoint: 0,
+ token0: new Token(ChainId.SMARTBCH, '0x0D8b355f9CEDeB612f2df4B39CdD87059A244567', 2, 'CANDYMAN', 'CandyMAN'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0xD4625760E0B5D9a0f46cB95dDa9b660fd6Db610A': {
+ farmId: 19,
+ allocPoint: 0,
+ token0: new Token(ChainId.SMARTBCH, '0x225FCa2A940cd5B18DFb168cD9B7f921C63d7B6E', 18, 'FIRE', 'Incinerate'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0xC28f5F07B733862f021f2266B99F5214c68E95d0': {
+ farmId: 20,
+ allocPoint: 0,
+ token0: new Token(ChainId.SMARTBCH, '0x7ebeadb95724a006afaf2f1f051b13f4ebebf711', 2, 'KITTEN', 'CashKitten'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0x1CC824B67e694fd5e0cC7D55120355B1AE4B9c50': {
+ farmId: 21,
+ allocPoint: 0,
+ token0: new Token(ChainId.SMARTBCH, '0x9192940099fDB2338B928DE2cad9Cd1525fEa881', 18, 'BPAD', 'BCHPad'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0xfe323f2898810E6C3c2c5A9E7dF78A116fFAD4fa': {
+ farmId: 22,
+ allocPoint: 0,
+ token0: new Token(ChainId.SMARTBCH, '0xffa2394b61d3de16538a2bbf3491297cc5a7c79a', 18, 'UATX', 'UatX Token'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0x365Ec450d670455b336b833Ca363d21b4de3B9E3': {
+ farmId: 23,
+ allocPoint: 0,
+ token0: new Token(ChainId.SMARTBCH, '0x4F1480ba79F7477230ec3b2eCc868E8221925072', 18, 'KONRA', 'Konra'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0x7FbcD4B5b7838F3C22151d492cB7E30B28dED77a': {
+ farmId: 24,
+ allocPoint: 999999,
+ token0: new Token(ChainId.SMARTBCH, '0x98Ff640323C059d8C4CB846976973FEEB0E068aA', 18, 'XTANGO', 'TANGObar'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0x0152E5F007D85aae58Eb7191Bd484f12F9c13052': {
+ farmId: 25,
+ allocPoint: 0,
+ token0: new Token(ChainId.SMARTBCH, '0x49F9ECF126B6dDF51C731614755508A4674bA7eD', 18, 'RMZ', 'Xolos'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0xC073d247f8FdB539Bc6653b6bfFEF8c61092738F': {
+ farmId: 26,
+ allocPoint: 999999,
+ token0: new Token(ChainId.SMARTBCH, '0x98Dd7eC28FB43b3C4c770AE532417015fa939Dd3', 18, 'FLEX', 'FLEX Coin'),
+ token1: TANGO[ChainId.SMARTBCH],
+ },
+ '0xcdb6081DCb9fd2b3d48927790DF7757E8d137fF4': {
+ farmId: 27,
+ allocPoint: 4499999,
+ token0: new Token(ChainId.SMARTBCH, '0x98Ff640323C059d8C4CB846976973FEEB0E068aA', 18, 'XTANGO', 'TANGObar'),
+ token1: FLEXUSD,
+ },
+ '0xfa2A542B0BF8F5e92Af0D1045ebF0abBB0A6C093': {
+ farmId: 28,
+ allocPoint: 26999999,
+ token0: new Token(ChainId.SMARTBCH, '0x4b85a666dec7c959e88b97814e46113601b07e57', 18, 'GOC', 'GoCrypto'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0x018005da1a5C886Fb48eB18Eda0849a26B99DA80': {
+ farmId: 29,
+ allocPoint: 0,
+ token0: new Token(ChainId.SMARTBCH, '0x77d4b6e44a53bbda9a1d156b32bb53a2d099e53d', 18, '1BCH', '1BCH'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0x864c0090D955D947D809CF315E17665Bf9e3b6aB': {
+ farmId: 30,
+ allocPoint: 0,
+ token0: new Token(ChainId.SMARTBCH, '0x4b85a666dec7c959e88b97814e46113601b07e57', 18, 'GOC', 'GoCrypto'),
+ token1: TANGO[ChainId.SMARTBCH],
+ },
+ '0x4509Ff66a56cB1b80a6184DB268AD9dFBB79DD53': {
+ farmId: 32,
+ allocPoint: 2499999,
+ token0: new Token(ChainId.SMARTBCH, '0xF05bD3d7709980f60CD5206BddFFA8553176dd29', 18, 'SIDX', 'SmartIndex'),
+ token1: WBCH[ChainId.SMARTBCH],
+ },
+ '0xE7845D6df693BFD1b0b50AB2d17ac06964559c6b': {
+ farmId: 33,
+ allocPoint: 4999750,
+ token0: new Token(ChainId.SMARTBCH, '0xe1e655be6f50344e6dd708c27bd8d66492d6ecaf', 18, 'LAWUSD', 'lawUSD'),
+ token1: TANGO[ChainId.SMARTBCH],
+ },
+ },
+ [ChainId.SMARTBCH_AMBER]: {
+ '0x07DE6fc05597E0E4c92C83637A8a0CA411f3a769': {
+ farmId: 0,
+ allocPoint: 1000,
+ token0: WBCH[ChainId.SMARTBCH_AMBER],
+ token1: new Token(ChainId.SMARTBCH_AMBER, '0xC6F80cF669Ab9e4BE07B78032b4821ed5612A9ce', 18, 'sc', 'testcoin2'),
+ },
+ },
+ }
+
+ const hardcodedPairs2x = {
+ [ChainId.SMARTBCH]: {
+ '0xCFa5B1C5FaBF867842Ac3C25E729Fc3671d27c50': {
+ farmId: 0,
+ allocPoint: 1249938,
+ token0: new Token(ChainId.SMARTBCH, '0xc70c7718C7f1CCd906534C2c4a76914173EC2c44', 18, 'KTH', 'Knuth'),
+ token1: WBCH[ChainId.SMARTBCH],
+ rewarderId: '0xbA85D6bB454315A0fb65b205Fa48DBAff82A4019',
+ rewardToken: new Token(ChainId.SMARTBCH, '0xc70c7718C7f1CCd906534C2c4a76914173EC2c44', 18, 'KTH', 'Knuth'),
+ rewardPerSecond: '1000000000000000000',
+ },
+ '0xF463db65674426A58E9C3fE557FaaE338026ef39': {
+ farmId: 1,
+ allocPoint: 1249937,
+ token0: new Token(
+ ChainId.SMARTBCH,
+ '0x675E1d6FcE8C7cC091aED06A68D079489450338a',
+ 18,
+ 'ARG',
+ 'Bitcoin Cash Argentina'
+ ),
+ token1: WBCH[ChainId.SMARTBCH],
+ rewarderId: '0x3f28b9BE239038568D67f076a0ff9AEdEa5668d8',
+ rewardToken: new Token(
+ ChainId.SMARTBCH,
+ '0x675E1d6FcE8C7cC091aED06A68D079489450338a',
+ 18,
+ 'ARG',
+ 'Bitcoin Cash Argentina'
+ ),
+ rewardPerSecond: '2342000000000000000000',
+ },
+ '0x0152E5F007D85aae58Eb7191Bd484f12F9c13052': {
+ farmId: 2,
+ allocPoint: 499999,
+ token0: new Token(ChainId.SMARTBCH, '0x49F9ECF126B6dDF51C731614755508A4674bA7eD', 18, 'RMZ', 'Xolos'),
+ token1: WBCH[ChainId.SMARTBCH],
+ rewarderId: '0xefEf4dC16316Ae8c7AF00489b0e5FA52be68D1B6',
+ rewardToken: new Token(ChainId.SMARTBCH, '0x49F9ECF126B6dDF51C731614755508A4674bA7eD', 18, 'RMZ', 'Xolos'),
+ rewardPerSecond: '58330000000000',
+ },
+ '0xD513165b3bbC1Ca812Db7CBB60340DDf74903A1c': {
+ farmId: 3,
+ allocPoint: 15624,
+ token0: new Token(ChainId.SMARTBCH, '0xF2d4D9c65C2d1080ac9e1895F6a32045741831Cd', 2, 'HONK', 'Honk'),
+ token1: WBCH[ChainId.SMARTBCH],
+ rewarderId: '0x3f43FF8eF6715Eb6E76452a9d719f54eFa5372b1',
+ rewardToken: new Token(ChainId.SMARTBCH, '0xF2d4D9c65C2d1080ac9e1895F6a32045741831Cd', 2, 'HONK', 'Honk'),
+ rewardPerSecond: '2325',
+ },
+ '0x864c0090D955D947D809CF315E17665Bf9e3b6aB': {
+ farmId: 4,
+ allocPoint: 14999748,
+ token0: new Token(ChainId.SMARTBCH, '0x4b85a666dec7c959e88b97814e46113601b07e57', 18, 'GOC', 'GoCrypto'),
+ token1: TANGO[ChainId.SMARTBCH],
+ rewarderId: '0x3e9AFf4008F3D6E05697025acCb607021e36e1e6',
+ rewardToken: new Token(ChainId.SMARTBCH, '0x4b85a666dec7c959e88b97814e46113601b07e57', 18, 'GOC', 'GoCrypto'),
+ rewardPerSecond: '005787037000000000',
+ },
+ '0x9E59AAc21DaB7C89d0BDA99335386868539Af9B8': {
+ farmId: 5,
+ allocPoint: 1,
+ token0: new Token(ChainId.SMARTBCH, '0x0D8b355f9CEDeB612f2df4B39CdD87059A244567', 2, 'CANDYMAN', 'CandyMAN'),
+ token1: WBCH[ChainId.SMARTBCH],
+ rewarderId: '0xDc7D5F633F5721fa3Ff2B73B9396F0eAcE58ec0F',
+ rewardToken: new Token(
+ ChainId.SMARTBCH,
+ '0x0D8b355f9CEDeB612f2df4B39CdD87059A244567',
+ 2,
+ 'CANDYMAN',
+ 'CandyMAN'
+ ),
+ rewardPerSecond: '01',
+ },
+ '0x365Ec450d670455b336b833Ca363d21b4de3B9E3': {
+ farmId: 6,
+ allocPoint: 62499,
+ token0: new Token(ChainId.SMARTBCH, '0x4F1480ba79F7477230ec3b2eCc868E8221925072', 18, 'KONRA', 'Konra'),
+ token1: WBCH[ChainId.SMARTBCH],
+ rewarderId: '0x2F3056526014992b757a9F81D7B084e60a0Eb187',
+ rewardToken: new Token(ChainId.SMARTBCH, '0x4F1480ba79F7477230ec3b2eCc868E8221925072', 18, 'KONRA', 'Konra'),
+ rewardPerSecond: '000011580000000000',
+ },
+ '0x5109aABC359c5267B6d470f43414319dd8a3C123': {
+ farmId: 7,
+ allocPoint: 15624,
+ token0: new Token(ChainId.SMARTBCH, '0x0cb20466c0dd6454acf50ec26f3042ccc6362fa0', 18, 'NARATH', 'Narath'),
+ token1: WBCH[ChainId.SMARTBCH],
+ rewarderId: '0x1d42B726E32f41102BC265d8a1cD5a1751e8deD9',
+ rewardToken: new Token(ChainId.SMARTBCH, '0x0cb20466c0dd6454acf50ec26f3042ccc6362fa0', 18, 'NARATH', 'Narath'),
+ rewardPerSecond: '925000000000000000000',
+ },
+ '0x7B545548dabA183Fc779e656da09dF6bD2b94F88': {
+ farmId: 8,
+ allocPoint: 499998,
+ token0: new Token(
+ ChainId.SMARTBCH,
+ '0x4EA4A00E15B9E8FeE27eB6156a865525083e9F71',
+ 18,
+ 'Martin₿',
+ 'Africa Unite'
+ ),
+ token1: WBCH[ChainId.SMARTBCH],
+ rewarderId: '0x6C54582E1F7E0602F526267BEB4b073E35eB46a4',
+ rewardToken: new Token(
+ ChainId.SMARTBCH,
+ '0x4EA4A00E15B9E8FeE27eB6156a865525083e9F71',
+ 18,
+ 'Martin₿',
+ 'Africa Unite'
+ ),
+ rewardPerSecond: '66979000000000000000000',
+ },
+ '0x2a7c9D8E0E2286A596192C3C16Cc68979D331F29': {
+ farmId: 9,
+ allocPoint: 62499,
+ token0: new Token(ChainId.SMARTBCH, '0x387122d80A642581E5AD620696a37b98BB9272e7', 18, 'JOOST', 'Joost.energy'),
+ token1: WBCH[ChainId.SMARTBCH],
+ rewarderId: '0xA76F4318eDe44a205EAcFB5eF6EaF28b0A83AAb8',
+ rewardToken: new Token(
+ ChainId.SMARTBCH,
+ '0x387122d80A642581E5AD620696a37b98BB9272e7',
+ 18,
+ 'JOOST',
+ 'Joost.energy'
+ ),
+ rewardPerSecond: '800000000000000000',
+ },
+ },
+ [ChainId.SMARTBCH_AMBER]: {
+ '0xCFa5B1C5FaBF867842Ac3C25E729Fc3671d27c50': {
+ farmId: 0,
+ allocPoint: 1249937,
+ token0: new Token(ChainId.SMARTBCH, '0xc70c7718C7f1CCd906534C2c4a76914173EC2c44', 18, 'KTH', 'Knuth'),
+ token1: WBCH[ChainId.SMARTBCH],
+ rewarderId: '0xbA85D6bB454315A0fb65b205Fa48DBAff82A4019',
+ rewardToken: new Token(ChainId.SMARTBCH, '0xc70c7718C7f1CCd906534C2c4a76914173EC2c44', 18, 'KTH', 'Knuth'),
+ rewardPerSecond: '1000000000000000000',
+ },
+ },
+ }
+
+ const kashiPairs = [] // unused
+ const swapPairs = []
+ const farms2 = useFarms()
+ let farms = []
+
+ for (const [pairAddress, pair] of Object.entries(hardcodedPairs[chainId])) {
+ swapPairs.push({
+ id: pairAddress,
+ reserveUSD: '100000',
+ totalSupply: '1000',
+ timestamp: '1599830986',
+ token0: {
+ id: pair.token0.address,
+ name: pair.token0.name,
+ symbol: pair.token0.symbol,
+ decimals: pair.token0.decimals,
+ },
+ token1: {
+ id: pair.token1.address,
+ name: pair.token1.name,
+ symbol: pair.token1.symbol,
+ decimals: pair.token1.decimals,
+ },
+ })
+
+ const f = {
+ pair: pairAddress,
+ symbol: `${hardcodedPairs[chainId][pairAddress].token0.symbol}-${hardcodedPairs[chainId][pairAddress].token1.symbol}`,
+ // eslint-disable-next-line react-hooks/rules-of-hooks
+ pool: usePool(pairAddress),
+ allocPoint: pair.allocPoint,
+ balance: '1000000000000000000',
+ chef: 0,
+ id: pair.farmId,
+ pendingSushi: undefined,
+ pending: 0,
+ owner: {
+ id: MASTERCHEF_ADDRESS[chainId],
+ sushiPerBlock: '10000000000000000000',
+ totalAllocPoint: '999949984',
+ },
+ userCount: 1,
+ }
+ // eslint-disable-next-line react-hooks/rules-of-hooks
+ f.pendingSushi = usePendingSushi(f)
+ f.pending = Number.parseFloat(f.pendingSushi?.toFixed())
+
+ farms.push(f)
+ }
+
+ // console.log(farms);
+ const flexUSDTangoPool = farms[1].pool
+ const bchFlexUSDPool = farms[3].pool
+ const bchTangoPool = farms[2].pool
+ let bchPriceUSD = 0
+ let tangoPriceUSD = 0
+ let tangoPriceBCH = 0
+ if (bchFlexUSDPool.reserves) {
+ bchPriceUSD =
+ Number.parseFloat(bchFlexUSDPool.reserves[1].toFixed()) / Number.parseFloat(bchFlexUSDPool.reserves[0].toFixed())
+ }
+ if (flexUSDTangoPool.reserves) {
+ tangoPriceUSD =
+ 1 /
+ (Number.parseFloat(flexUSDTangoPool.reserves[0].toFixed()) /
+ Number.parseFloat(flexUSDTangoPool.reserves[1].toFixed()))
+ }
+ if (bchTangoPool.reserves) {
+ tangoPriceBCH =
+ Number.parseFloat(bchTangoPool.reserves[0].toFixed()) / Number.parseFloat(bchTangoPool.reserves[1].toFixed())
+ }
+
+ for (const [pairAddress, pair] of Object.entries(hardcodedPairs2x[chainId])) {
+ swapPairs.push({
+ id: pairAddress,
+ reserveUSD: '100000',
+ totalSupply: '1000',
+ timestamp: '1599830986',
+ token0: {
+ id: pair.token0.address,
+ name: pair.token0.name,
+ symbol: pair.token0.symbol,
+ decimals: pair.token0.decimals,
+ },
+ token1: {
+ id: pair.token1.address,
+ name: pair.token1.name,
+ symbol: pair.token1.symbol,
+ decimals: pair.token1.decimals,
+ },
+ })
+
+ const f = {
+ pair: pairAddress,
+ symbol: `${hardcodedPairs2x[chainId][pairAddress].token0.symbol}-${hardcodedPairs2x[chainId][pairAddress].token1.symbol}`,
+
+ // eslint-disable-next-line react-hooks/rules-of-hooks
+ pool: usePool(pairAddress),
+
+ allocPoint: pair.allocPoint,
+ balance: '1000000000000000000',
+ chef: 1,
+ id: pair.farmId,
+ pendingSushi: undefined,
+ pending: 0,
+ owner: {
+ id: MASTERCHEF_V2_ADDRESS[chainId],
+ sushiPerBlock: '10000000000000000000',
+ totalAllocPoint: '999949984', // "999949984"
+ },
+
+ rewarder: {
+ id: pair.rewarderId,
+ rewardToken: pair.rewardToken.address,
+ rewardPerSecond: pair.rewardPerSecond,
+ },
+
+ rewardToken: {
+ ...pair.rewardToken,
+ // eslint-disable-next-line react-hooks/rules-of-hooks
+ derivedETH: getTokenPriceInBch(usePool(pairAddress), pair, chainId, tangoPriceBCH, bchPriceUSD),
+ },
+ maxPrice: (100 * Math.random()).toFixed(4),
+ minPrice: (10 * Math.random()).toFixed(4),
+ userCount: 1,
+ }
+ // eslint-disable-next-line react-hooks/rules-of-hooks
+ f.pendingSushi = usePendingSushi(f)
+ f.pending = f.id % 2 === 0 ? 1 : 0
+ farms.push(f)
+ }
+
+ const [v2PairsBalances, fetchingV2PairBalances] = useTokenBalancesWithLoadingIndicator(
+ MASTERCHEF_ADDRESS[chainId],
+ farms.map((farm) => new Token(chainId, farm.pair, 18, 'LP', 'LP Token'))
+ )
+
+ const [v2PairsBalances2x, fetchingV2PairBalances2x] = useTokenBalancesWithLoadingIndicator(
+ MASTERCHEF_V2_ADDRESS[chainId],
+ farms.map((farm) => new Token(chainId, farm.pair, 18, 'LP', 'LP Token'))
+ )
+
+ if (!fetchingV2PairBalances) {
+ for (let i = 0; i < farms.length; ++i) {
+ if (v2PairsBalances.hasOwnProperty(farms[i].pair) && farms[i].pool.totalSupply) {
+ const totalSupply = Number.parseFloat(farms[i].pool.totalSupply.toFixed())
+ let chefBalance = Number.parseFloat(v2PairsBalances[farms[i].pair].toFixed())
+
+ if (v2PairsBalances2x.hasOwnProperty(farms[i].pair)) {
+ chefBalance += Number.parseFloat(v2PairsBalances2x[farms[i].pair].toFixed())
+ }
+
+ let tvl = 0
+ if (farms[i].pool.token0 === TANGO[chainId].address) {
+ const reserve = Number.parseFloat(farms[i].pool.reserves[0].toFixed())
+ tvl = (reserve / totalSupply) * chefBalance * tangoPriceUSD * 2
+ } else if (farms[i].pool.token1 === TANGO[chainId].address) {
+ const reserve = Number.parseFloat(farms[i].pool.reserves[1].toFixed())
+ tvl = (reserve / totalSupply) * chefBalance * tangoPriceUSD * 2
+ } else if (farms[i].pool.token0 === FLEXUSD.address) {
+ const reserve = Number.parseFloat(farms[i].pool.reserves[0].toFixed())
+ tvl = (reserve / totalSupply) * chefBalance * 2
+ } else if (farms[i].pool.token1 === FLEXUSD.address) {
+ const reserve = Number.parseFloat(farms[i].pool.reserves[1].toFixed())
+ tvl = (reserve / totalSupply) * chefBalance * 2
+ } else if (farms[i].pool.token0 === WBCH[chainId].address) {
+ const reserve = Number.parseFloat(farms[i].pool.reserves[0].toFixed())
+ tvl = (reserve / totalSupply) * chefBalance * bchPriceUSD * 2
+ } else if (farms[i].pool.token1 === WBCH[chainId].address) {
+ const reserve = Number.parseFloat(farms[i].pool.reserves[1].toFixed())
+ tvl = (reserve / totalSupply) * chefBalance * bchPriceUSD * 2
+ }
+ farms[i].tvl = tvl
+ farms[i].chefBalance = chefBalance
+ } else {
+ farms[i].tvl = '0'
+ farms[i].chefBalance = 0
+ }
+ }
+ }
+
+ const positions = usePositions(chainId)
+
+ // const averageBlockTime = useAverageBlockTime()
+ const averageBlockTime = 6
+ const masterChefV1TotalAllocPoint = useMasterChefV1TotalAllocPoint()
+ const masterChefV1SushiPerBlock = useMasterChefV1SushiPerBlock()
+
+ const blocksPerDay = 86400 / Number(averageBlockTime)
+
+ const map = (pool) => {
+ // TODO: Account for fees generated in case of swap pairs, and use standard compounding
+ // algorithm with the same intervals acrosss chains to account for consistency.
+ // For lending pairs, what should the equivilent for fees generated? Interest gained?
+ // How can we include this?
+
+ // TODO: Deal with inconsistencies between properties on subgraph
+ pool.owner = pool?.owner || pool?.masterChef
+ pool.balance = pool?.balance || pool?.slpBalance
+
+ const swapPair = swapPairs?.find((pair) => pair.id === pool.pair)
+ const kashiPair = kashiPairs?.find((pair) => pair.id === pool.pair)
+
+ const type = swapPair ? PairType.SWAP : PairType.KASHI
+
+ const pair = swapPair || kashiPair
+
+ const blocksPerDay = 15684 // calculated empirically
+
+ function getRewards() {
+ // TODO: Some subgraphs give sushiPerBlock & sushiPerSecond, and mcv2 gives nothing
+ const sushiPerBlock =
+ pool?.owner?.sushiPerBlock / 1e18 ||
+ (pool?.owner?.sushiPerSecond / 1e18) * averageBlockTime ||
+ masterChefV1SushiPerBlock
+
+ const rewardPerBlock = (pool.allocPoint / pool.owner.totalAllocPoint) * sushiPerBlock
+
+ const defaultReward = {
+ token: 'TANGO',
+ icon: 'https://raw.githubusercontent.com/tangoswap-cash/assets/master/blockchains/smartbch/assets/0x73BE9c8Edf5e951c9a0762EA2b1DE8c8F38B5e91/logo.png',
+ rewardPerBlock,
+ rewardPerDay: rewardPerBlock * blocksPerDay,
+ rewardPrice: +tangoPriceUSD,
+ }
+
+ let rewards = [defaultReward]
+
+ if (pool.chef === Chef.MASTERCHEF_V2) {
+ // override for mcv2...
+ pool.owner.totalAllocPoint = masterChefV1TotalAllocPoint
+
+ const icon = `https://raw.githubusercontent.com/tangoswap-cash/assets/master/blockchains/smartbch/assets/${getAddress(
+ pool.rewarder.rewardToken
+ )}/logo.png`
+
+ const decimals = 10 ** pool.rewardToken.decimals
+ // console.log("pool.rewardToken.decimals: ", pool.rewardToken.decimals);
+ // console.log("pool.rewardToken.derivedETH: ", pool.rewardToken.derivedETH);
+ // console.log("pool.rewarder.rewardPerSecond: ", pool.rewarder.rewardPerSecond);
+ // console.log("decimals: ", decimals);
+
+ if (pool.rewarder.rewardToken !== '0x0000000000000000000000000000000000000000') {
+ // console.log("pool.rewarder.rewardPerSecond / decimals: ", pool.rewarder.rewardPerSecond / decimals);
+
+ const rewardPerBlock = (pool.rewarder.rewardPerSecond / decimals) * averageBlockTime
+
+ // console.log("rewardPerBlock: ", rewardPerBlock);
+
+ const rewardPerDay = (pool.rewarder.rewardPerSecond / decimals) * averageBlockTime * blocksPerDay
+ const rewardPrice = pool.rewardToken.derivedETH * bchPriceUSD
+
+ // console.log("rewardPrice: ", rewardPrice);
+
+ const reward = {
+ token: pool.rewardToken.symbol,
+ icon: icon,
+ rewardPerBlock,
+ rewardPerDay,
+ rewardPrice,
+ }
+
+ rewards[1] = reward
+ }
+ }
+
+ return rewards
+ }
+
+ const rewards = getRewards()
+
+ const balance = Number(pool.balance / 1e18)
+
+ const roiPerBlock =
+ rewards.reduce((previousValue, currentValue) => {
+ return previousValue + currentValue.rewardPerBlock * currentValue.rewardPrice
+ }, 0) / pool.tvl
+
+ const roiPerDay = roiPerBlock * blocksPerDay
+ const roiPerYear = roiPerDay * 365
+
+ // console.log("rewards: ", rewards);
+ // console.log("roiPerBlock: ", roiPerBlock);
+ // console.log("roiPerDay: ", roiPerDay);
+ // console.log("roiPerYear: ", roiPerYear);
+
+ const position = positions.find((position) => position.id === pool.id && position.chef === pool.chef)
+
+ return {
+ ...pool,
+ ...position,
+ pair: {
+ ...pair,
+ decimals: pair.type === PairType.KASHI ? Number(pair.asset.tokenInfo.decimals) : 18,
+ type,
+ },
+ balance,
+ roiPerYear,
+ rewards,
+ }
+ }
+
+ const FILTER = {
+ all: (farm) => farm.allocPoint !== 0,
+ portfolio: (farm) => farm.pending !== 0,
+ past: (farm) => farm.allocPoint === 0,
+ // sushi: (farm) => farm.pair.type === PairType.SWAP && farm.allocPoint !== '0',
+ // kashi: (farm) => farm.pair.type === PairType.KASHI && farm.allocPoint !== '0',
+ // '2x': (farm) => (farm.chef === Chef.MASTERCHEF_V2) && farm.allocPoint !== '0',
+
+ '2x': (farm) => farm.chef === Chef.MASTERCHEF_V2 && farm.rewards.length > 1 && farm.allocPoint !== '0',
+ }
+
+ const data = farms
+ .filter((farm) => {
+ return (
+ (swapPairs && swapPairs.find((pair) => pair.id === farm.pair)) ||
+ (kashiPairs && kashiPairs.find((pair) => pair.id === farm.pair))
+ )
+ })
+ .map(map)
+ .filter((farm) => {
+ return type in FILTER ? FILTER[type](farm) : true
+ })
+
+ const options = {
+ keys: ['pair.id', 'pair.token0.symbol', 'pair.token1.symbol'],
+ threshold: 0.4,
+ }
+
+ const { result, term, search } = useFuse({
+ data,
+ options,
+ })
+
+ const basePath = 'robots/robots-list'
+
+ const optionsMenu = [
+ {
+ href: `/${basePath}?filter=portfolio`,
+ label: 'Your Robots',
+ exact: true
+ },
+ {
+ divider: true
+ },
+ {
+ href: `/${basePath}?filter=all`,
+ label: 'All Robots'
+ },
+ ]
+
+ return (
+
+
+ Robots | Orders.Cash
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}