diff --git a/package.json b/package.json
index c7f14951..006a38b7 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,7 @@
"@types/styled-components": "^5.1.9",
"@types/wcag-contrast": "^3.0.0",
"@typescript-eslint/eslint-plugin": "^4.33.0",
- "@typescript-eslint/parser": "^4.28.0",
+ "@typescript-eslint/parser": "^4.33.0",
"@ubeswap/injected-connector": "^6.1.0",
"@uniswap/token-lists": "^1.0.0-beta.24",
"@web3-react/core": "^6.1.9",
@@ -134,7 +134,7 @@
"@ubeswap/core": "^1.0.2",
"@ubeswap/default-token-list": "^4.1.39",
"@ubeswap/moola": "^0.2.0",
- "@ubeswap/sdk": "^2.1.1",
+ "@ubeswap/sdk": "^2.1.2",
"@uniswap/default-token-list": "^2.0.0",
"ajv-formats": "^2.1.1",
"eventemitter3": "^4.0.7",
diff --git a/public/locales/en.json b/public/locales/en.json
index 36384b2c..2c46b3d4 100644
--- a/public/locales/en.json
+++ b/public/locales/en.json
@@ -229,5 +229,8 @@
"farmUBE": "Farm UBE",
"youHaveUnclaimedRewards": "You have {{count}} farms with unclaimed rewards",
"claimAllRewards": "Claim all rewards",
- "featuredPools": "Featured Pools"
+ "featuredPools": "Featured Pools",
+ "limitOrder": "Limit Order",
+ "price": "Price",
+ "placeOrder": "Place Order"
}
diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx
index 97e0c07c..b0fde538 100644
--- a/src/components/Button/index.tsx
+++ b/src/components/Button/index.tsx
@@ -337,3 +337,20 @@ export function ButtonRadio({ active, ...rest }: { active?: boolean } & ButtonPr
return
}
}
+
+export const TabButton = styled(ButtonLight)<{
+ active?: boolean
+}>`
+ background-color: initial;
+ width: 35%;
+ ${({ active }) =>
+ active &&
+ `
+ box-shadow: 0 0 0 1pt #6D619A70;
+ background-color: #6D619A70;
+`}
+ font-size: 12px;
+ display: inline-block;
+ padding: 0.75rem;
+ margin: 0.5rem 0.05rem 0.5rem 0.5rem;
+`
diff --git a/src/components/Column/index.tsx b/src/components/Column/index.tsx
index bb046e9a..f5813d8f 100644
--- a/src/components/Column/index.tsx
+++ b/src/components/Column/index.tsx
@@ -25,5 +25,8 @@ export const TopSection = styled(AutoColumn)`
width: 100%;
margin-bottom: 24px;
`
+export const TopSectionLimitOrder = styled(TopSection)`
+ max-width: 420px;
+`
export default Column
diff --git a/src/components/Header/BridgeMenuGroup.tsx b/src/components/Header/BridgeMenuGroup.tsx
index 3e2d18f2..e1551e91 100644
--- a/src/components/Header/BridgeMenuGroup.tsx
+++ b/src/components/Header/BridgeMenuGroup.tsx
@@ -1,4 +1,3 @@
-import { darken } from 'polished'
import React, { useRef } from 'react'
import styled from 'styled-components'
@@ -6,33 +5,7 @@ import { useOnClickOutside } from '../../hooks/useOnClickOutside'
import { ApplicationModal } from '../../state/application/actions'
import { useModalOpen, useToggleModal } from '../../state/application/hooks'
import { ExternalLink } from '../../theme'
-
-const StyledNavMenu = styled('div')`
- ${({ theme }) => theme.flexRowNoWrap}
- align-items: left;
- border-radius: 3rem;
- outline: none;
- cursor: pointer;
- text-decoration: none;
- color: ${({ theme }) => theme.text2};
- font-size: 1rem;
- width: fit-content;
- margin: 0 12px;
- font-weight: 500;
-
- :hover,
- :focus {
- color: ${({ theme }) => darken(0.1, theme.text1)};
- }
-
- @media (max-width: 320px) {
- margin: 0 8px;
- }
-
- ${({ theme }) => theme.mediaWidth.upToExtraSmall`
- display: none;
- `}
-`
+import { StyledNavMenu } from './NavMenu'
const StyledMenu = styled.div`
display: flex;
@@ -93,6 +66,9 @@ export default function BridgeMenuGroup() {
+
)}
diff --git a/src/components/Header/ChartsMenuGroup.tsx b/src/components/Header/ChartsMenuGroup.tsx
new file mode 100644
index 00000000..df679185
--- /dev/null
+++ b/src/components/Header/ChartsMenuGroup.tsx
@@ -0,0 +1,70 @@
+import React, { useRef } from 'react'
+import styled from 'styled-components'
+
+import { useOnClickOutside } from '../../hooks/useOnClickOutside'
+import { ApplicationModal } from '../../state/application/actions'
+import { useModalOpen, useToggleModal } from '../../state/application/hooks'
+import { ExternalLink } from '../../theme'
+import { StyledNavMenu } from './NavMenu'
+
+const StyledMenu = styled.div`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ position: relative;
+`
+
+const MenuFlyout = styled.span`
+ min-width: 8.125rem;
+ background-color: ${({ theme }) => theme.bg3};
+ 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);
+ border-radius: 12px;
+ padding: 0.5rem;
+ display: flex;
+ flex-direction: column;
+ font-size: 1rem;
+ position: absolute;
+ top: 3rem;
+ right: 0rem;
+ z-index: 100;
+`
+
+const MenuItem = styled(ExternalLink)`
+ flex: 1;
+ padding: 0.5rem 0.5rem;
+ color: ${({ theme }) => theme.text2};
+ :hover {
+ color: ${({ theme }) => theme.text1};
+ cursor: pointer;
+ text-decoration: none;
+ }
+ > svg {
+ margin-right: 8px;
+ }
+`
+
+export default function ChartsMenuGroup() {
+ const node = useRef()
+ const open = useModalOpen(ApplicationModal.CHARTS)
+ const toggle = useToggleModal(ApplicationModal.CHARTS)
+ useOnClickOutside(node, open ? toggle : undefined)
+
+ return (
+ // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451
+
+ Charts
+
+ {open && (
+
+
+
+
+ )}
+
+ )
+}
diff --git a/src/components/Header/NavMenu.tsx b/src/components/Header/NavMenu.tsx
new file mode 100644
index 00000000..5786c6c2
--- /dev/null
+++ b/src/components/Header/NavMenu.tsx
@@ -0,0 +1,29 @@
+import { darken } from 'polished'
+import styled from 'styled-components'
+
+export const StyledNavMenu = styled('div')`
+ ${({ theme }) => theme.flexRowNoWrap}
+ align-items: left;
+ border-radius: 3rem;
+ outline: none;
+ cursor: pointer;
+ text-decoration: none;
+ color: ${({ theme }) => theme.text2};
+ font-size: 1rem;
+ width: fit-content;
+ margin: 0 12px;
+ font-weight: 500;
+
+ :hover,
+ :focus {
+ color: ${({ theme }) => darken(0.1, theme.text1)};
+ }
+
+ @media (max-width: 320px) {
+ margin: 0 8px;
+ }
+
+ ${({ theme }) => theme.mediaWidth.upToExtraSmall`
+ display: none;
+ `}
+`
diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx
index 7c39c22c..8da99ffa 100644
--- a/src/components/Header/index.tsx
+++ b/src/components/Header/index.tsx
@@ -29,6 +29,7 @@ import Menu from '../Menu'
import Row, { RowFixed } from '../Row'
import Web3Status from '../Web3Status'
import BridgeMenuGroup from './BridgeMenuGroup'
+import ChartsMenuGroup from './ChartsMenuGroup'
import UbeBalanceContent from './UbeBalanceContent'
const HeaderFrame = styled.div`
@@ -382,6 +383,9 @@ export default function Header() {
{t('swap')}
+
+ {t('limitOrder')}
+
{t('stake')}
-
- {t('charts')} ↗
-
+
@@ -417,6 +419,16 @@ export default function Header() {
onClose={onDrawerClose}
>
+
+
+ {t('stake')}
+
+
+
+
+ Limit Orders
+
+
Bridge
@@ -437,16 +449,26 @@ export default function Header() {
Optics
+
+
+ Orbit
+
+
-
- {t('stake')}
+
+ Charts
-
-
- {t('charts')}
+
+
+ Analytics
-
+
+
+
+ Celo Tracker
+
+
diff --git a/src/components/LimitOrderHistory/LimitOrderHistoryBody.tsx b/src/components/LimitOrderHistory/LimitOrderHistoryBody.tsx
new file mode 100644
index 00000000..ee42df19
--- /dev/null
+++ b/src/components/LimitOrderHistory/LimitOrderHistoryBody.tsx
@@ -0,0 +1,11 @@
+import { BodyWrapper } from 'pages/AppBody'
+import React from 'react'
+import styled from 'styled-components'
+
+const LimitOrderHistoryBodyWrapper = styled(BodyWrapper)`
+ margin-top: 2rem;
+`
+
+export default function LimitOrderHistoryBody({ children }: { children: React.ReactNode }) {
+ return {children}
+}
diff --git a/src/components/LimitOrderHistory/LimitOrderHistoryItem.tsx b/src/components/LimitOrderHistory/LimitOrderHistoryItem.tsx
new file mode 100644
index 00000000..02d905ff
--- /dev/null
+++ b/src/components/LimitOrderHistory/LimitOrderHistoryItem.tsx
@@ -0,0 +1,197 @@
+import { useContractKit } from '@celo-tools/use-contractkit'
+import { ChainId as UbeswapChainId, JSBI, Token, TokenAmount } from '@ubeswap/sdk'
+import { BigNumber } from 'ethers'
+import { useToken } from 'hooks/Tokens'
+import { useOrderBookContract, useOrderBookRewardDistributorContract } from 'hooks/useContract'
+import { BPS_DENOMINATOR } from 'pages/LimitOrder'
+import React from 'react'
+import { useSingleCallResult } from 'state/multicall/hooks'
+import styled from 'styled-components'
+
+import { ORDER_BOOK_ADDRESS, ORDER_BOOK_REWARD_DISTRIBUTOR_ADDRESS } from '../../constants'
+import useTheme from '../../hooks/useTheme'
+import { useCancelOrderCallback } from '../../pages/LimitOrder/useCancelOrderCallback'
+import { ExternalLink, LinkIcon, TYPE } from '../../theme'
+import { RowFlat } from '../Row'
+
+const Container = styled.div<{
+ lastDisplayItem?: boolean
+}>`
+ background-color: ${({ theme }) => theme.bg1};
+ margin-bottom: 2rem;
+ padding-bottom: 1rem;
+ padding-left: 0.5rem;
+ border-bottom: 2px solid ${({ theme }) => theme.primary5};
+ ${({ lastDisplayItem }) =>
+ lastDisplayItem &&
+ `
+border-bottom-style: none;
+`}
+`
+
+const SymbolContainer = styled.div`
+ width: 75%;
+`
+
+const AssetSymbol = styled.div`
+ border-radius: 12px;
+ border: 1px solid ${({ theme }) => theme.primary5};
+ padding: 0.5rem;
+`
+
+const AssetRow = styled(RowFlat)`
+ margin-bottom: 0.5rem;
+`
+const SellText = styled.div`
+ font-weight: 700;
+ margin-top: 0.25rem;
+`
+
+const OrderToFill = styled.div`
+ font-weight: 300;
+ font-size: 14px;
+ margin-top: 0.25rem;
+`
+
+const StyledControlButton = styled.button`
+ height: 24px;
+ background-color: ${({ theme }) => theme.red1};
+ border: 1px solid ${({ theme }) => theme.red2};
+ border-radius: 0.5rem;
+ font-size: 0.875rem;
+ font-weight: 500;
+ cursor: pointer;
+ margin-left: 7rem;
+ margin-right: 2rem;
+ color: white;
+ :hover {
+ border: 1px solid ${({ theme }) => theme.red3};
+ }
+ :focus {
+ border: 1px solid ${({ theme }) => theme.red3};
+ outline: none;
+ }
+
+ ${({ theme }) => theme.mediaWidth.upToExtraSmall`
+ margin-left: 0.4rem;
+ margin-right: 0.1rem;
+ `};
+`
+
+const AddressLink = styled(ExternalLink)`
+ font-size: 0.825rem;
+ color: ${({ theme }) => theme.text3};
+ border-radius: 12px;
+ width: 45%;
+ padding: 0.25rem;
+ margin-top: 0.5rem;
+ border: 1px solid ${({ theme }) => theme.primary5};
+ font-size: 0.825rem;
+ display: flex;
+ :hover {
+ color: ${({ theme }) => theme.text2};
+ }
+`
+
+const BaselineRow = styled(AssetRow)`
+ align-items: baseline;
+`
+
+interface LimitOrderHistoryItemProps {
+ item: {
+ orderHash: string
+ makingAmount: BigNumber
+ takingAmount: BigNumber
+ makerAsset: string
+ takerAsset: string
+ remaining: BigNumber
+ isOrderOpen: boolean
+ transactionHash: string
+ }
+ rewardCurrency: Token | undefined
+ lastDisplayItem: boolean
+}
+
+export default function LimitOrderHistoryItem({ item, rewardCurrency, lastDisplayItem }: LimitOrderHistoryItemProps) {
+ const { network } = useContractKit()
+ const chainId = network.chainId as unknown as UbeswapChainId
+ const { callback: cancelOrder } = useCancelOrderCallback(item.orderHash)
+ const theme = useTheme()
+ const makerToken = useToken(item.makerAsset)
+ const takerToken = useToken(item.takerAsset)
+
+ const transactionLink = `${network.explorer}/tx/${item.transactionHash}`
+
+ const orderBookContract = useOrderBookContract(ORDER_BOOK_ADDRESS[chainId as unknown as UbeswapChainId])
+ const orderBookFee = useSingleCallResult(orderBookContract, 'fee', []).result?.[0]
+ const rewardDistributorContract = useOrderBookRewardDistributorContract(
+ ORDER_BOOK_REWARD_DISTRIBUTOR_ADDRESS[chainId]
+ )
+ // TODO: This should really be based on the latest rewardRate change event from the logs
+ const rewardRate = useSingleCallResult(rewardDistributorContract, 'rewardRate', [makerToken?.address]).result?.[0]
+
+ if (!makerToken || !takerToken) {
+ return null
+ }
+
+ const makingAmount = new TokenAmount(makerToken, item.makingAmount.toString())
+ const reward =
+ rewardCurrency && rewardRate
+ ? new TokenAmount(
+ rewardCurrency,
+ JSBI.divide(JSBI.multiply(makingAmount.raw, JSBI.BigInt(rewardRate.toString())), BPS_DENOMINATOR)
+ )
+ : undefined
+ const takingAmount = new TokenAmount(takerToken, item.takingAmount.toString())
+ const remaining = new TokenAmount(makerToken, item.remaining.toString())
+
+ return (
+
+
+
+
+ {makerToken.symbol}
+
+ ➜
+
+ {takerToken.symbol}
+
+
+ {item.isOrderOpen && (
+ cancelOrder && cancelOrder()}>Cancel
+ )}
+
+
+ {makingAmount.toSignificant(4)} {makerToken.symbol} for {takingAmount.toSignificant(4)} {takerToken.symbol}
+
+ {item.isOrderOpen && (
+
+ Remaining Order to Fill: {remaining.toSignificant(4)} {makerToken.symbol}
+
+ )}
+
+ Order Placement Fee:{' '}
+ {orderBookFee
+ ? makingAmount.multiply(orderBookFee.toString()).divide(BPS_DENOMINATOR.toString()).toSignificant(4)
+ : '-'}{' '}
+ {makerToken.symbol}
+
+ {reward?.greaterThan('0') ? (
+
+ Order Reward: {reward.toSignificant(4)} {reward.currency.symbol}
+
+ ) : (
+ Order Reward: -
+ )}
+ {item.isOrderOpen && (
+
+
+ View Transaction
+
+ )}
+
+ )
+}
diff --git a/src/components/PriceInputPanel/index.tsx b/src/components/PriceInputPanel/index.tsx
new file mode 100644
index 00000000..44e3c83d
--- /dev/null
+++ b/src/components/PriceInputPanel/index.tsx
@@ -0,0 +1,68 @@
+import React from 'react'
+import { useTranslation } from 'react-i18next'
+import styled from 'styled-components'
+
+import { TYPE } from '../../theme'
+import { Input as NumericalInput } from '../NumericalInput'
+
+const InputRow = styled.div<{ selected: boolean }>`
+ ${({ theme }) => theme.flexRowNoWrap}
+ align-items: center;
+ padding: ${({ selected }) => (selected ? '0.75rem 0.5rem 0.75rem 1rem' : '0.75rem 0.75rem 0.75rem 1rem')};
+`
+
+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;
+`
+
+const Container = styled.div<{ hideInput: boolean }>`
+ border-radius: ${({ hideInput }) => (hideInput ? '8px' : '20px')};
+ border: 1px solid ${({ theme }) => theme.bg2};
+ background-color: ${({ theme }) => theme.bg1};
+`
+
+interface PriceInputPanelProps {
+ value: string
+ placeholder?: string
+ onUserInput: (value: string) => void
+ disableCurrencySelect?: boolean
+ hideInput?: boolean
+ id: string
+}
+
+export default function PriceInputPanel({
+ value,
+ placeholder,
+ onUserInput,
+ disableCurrencySelect = false,
+ hideInput = false,
+ id,
+}: PriceInputPanelProps) {
+ const { t } = useTranslation()
+
+ return (
+
+
+
+ {!hideInput && (
+ <>
+ {t('price')}
+ {
+ onUserInput(val)
+ }}
+ placeholder={placeholder}
+ />
+ >
+ )}
+
+
+
+ )
+}
diff --git a/src/components/Stake/StakeInputField.tsx b/src/components/Stake/StakeInputField.tsx
index 3c828737..825a1bb7 100644
--- a/src/components/Stake/StakeInputField.tsx
+++ b/src/components/Stake/StakeInputField.tsx
@@ -4,11 +4,8 @@ import { darken } from 'polished'
import React from 'react'
import styled from 'styled-components'
-import useTheme from '../../hooks/useTheme'
import { useCurrencyBalance } from '../../state/wallet/hooks'
-import { TYPE } from '../../theme'
import { Input as NumericalInput } from '../NumericalInput'
-import { RowBetween } from '../Row'
const InputRow = styled.div<{ selected: boolean }>`
${({ theme }) => theme.flexRowNoWrap}
@@ -68,6 +65,27 @@ const StyledControlButton = styled.button`
margin-right: 0.1rem;
`};
`
+
+const AmountWrapper = styled.div`
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-between;
+ width: 100%;
+`
+
+const AmountDescriptionWrapper = styled.div`
+ display: flex;
+ justify-content: space-between;
+ width: unset;
+ cursor: pointer;
+ font-weight: 500;
+ font-size: 14px;
+ margin-bottom: 0.5rem;
+ color: ${({ theme }) => theme.text2};
+ ${({ theme }) => theme.mediaWidth.upToExtraSmall`
+ width: 100%;
+`};
+`
const ButtonGroup = styled.div``
interface StakeInputFieldProps {
@@ -78,7 +96,6 @@ interface StakeInputFieldProps {
hideBalance?: boolean
hideInput?: boolean
id: string
- customBalanceText?: string
chainId?: ChainId
stakeBalance?: TokenAmount
walletBalance?: TokenAmount
@@ -92,7 +109,6 @@ export default function StakeInputField({
hideBalance = false,
hideInput = false,
id,
- customBalanceText,
stakeBalance,
walletBalance,
}: StakeInputFieldProps) {
@@ -100,41 +116,33 @@ export default function StakeInputField({
const userBalance = useCurrencyBalance(account ?? undefined, currency ?? undefined)
const selectedCurrencyBalance = walletBalance ?? userBalance
- const theme = useTheme()
return (
{!hideInput && (
-
+
{account && (
<>
-
- {'Current Stake: ' + (stakeBalance ? stakeBalance.toFixed(2, { groupSeparator: ',' }) : '--')}
-
-
- {!hideBalance && !!currency && selectedCurrencyBalance
- ? (customBalanceText ?? 'Wallet Balance: ') + selectedCurrencyBalance?.toSignificant(4)
- : ' -'}
-
+
+ Current Stake:
+ {stakeBalance ? stakeBalance.toFixed(2, { groupSeparator: ',' }) : '--'}
+
+
+ Wallet Balance:
+
+ {!hideBalance && !!currency && selectedCurrencyBalance
+ ? selectedCurrencyBalance?.toSignificant(4)
+ : '--'}
+
+
>
)}
-
+
)}
-
+
{!hideInput && (
<>
void
+ onConfirm: () => void
+ swapErrorMessage: string | undefined
+ onDismiss: () => void
+}) {
+ const { t } = useTranslation()
+ const showAcceptChanges = useMemo(
+ () => Boolean(trade && originalTrade && tradeMeaningfullyDiffers(trade, originalTrade)),
+ [originalTrade, trade]
+ )
+ const modalHeader = useCallback(() => {
+ return trade ? (
+
+ ) : null
+ }, [allowedSlippage, onAcceptChanges, recipient, showAcceptChanges, trade])
+
+ const modalBottom = useCallback(() => {
+ return trade ? (
+
+ ) : null
+ }, [allowedSlippage, onConfirm, showAcceptChanges, swapErrorMessage, trade])
+
+ // text to show while loading
+ const pendingText = `Swapping ${trade?.inputAmount?.toSignificant(6)} ${
+ trade?.inputAmount?.currency?.symbol
+ } for ${trade?.outputAmount?.toSignificant(6)} ${trade?.outputAmount?.currency?.symbol}`
+
+ const confirmationContent = useCallback(
+ () =>
+ swapErrorMessage ? (
+
+ ) : (
+
+ ),
+ [swapErrorMessage, onDismiss, t, modalHeader, modalBottom]
+ )
+
+ return (
+
+ )
+}
diff --git a/src/components/swap/LimitOrderModalHeader.tsx b/src/components/swap/LimitOrderModalHeader.tsx
new file mode 100644
index 00000000..49eb73f1
--- /dev/null
+++ b/src/components/swap/LimitOrderModalHeader.tsx
@@ -0,0 +1,132 @@
+import { Trade, TradeType } from '@ubeswap/sdk'
+import React, { useContext, useMemo } from 'react'
+import { AlertTriangle, ArrowDown } from 'react-feather'
+import { Text } from 'rebass'
+import { ThemeContext } from 'styled-components'
+
+import { Field } from '../../state/swap/actions'
+import { TYPE } from '../../theme'
+import { isAddress, shortenAddress } from '../../utils'
+import { computeSlippageAdjustedAmounts, computeTradePriceBreakdown, warningSeverity } from '../../utils/prices'
+import { ButtonPrimary } from '../Button'
+import { AutoColumn } from '../Column'
+import CurrencyLogo from '../CurrencyLogo'
+import { RowBetween, RowFixed } from '../Row'
+import { MoolaDirectTrade } from './routing/moola/MoolaDirectTrade'
+import { SwapShowAcceptChanges, TruncatedText } from './styleds'
+
+export default function LimitOrderModalHeader({
+ trade,
+ allowedSlippage,
+ recipient,
+ showAcceptChanges,
+ onAcceptChanges,
+}: {
+ trade: Trade
+ allowedSlippage: number
+ recipient: string | null
+ showAcceptChanges: boolean
+ onAcceptChanges: () => void
+}) {
+ const slippageAdjustedAmounts = useMemo(
+ () => computeSlippageAdjustedAmounts(trade, allowedSlippage),
+ [trade, allowedSlippage]
+ )
+ const { priceImpactWithoutFee } = useMemo(() => computeTradePriceBreakdown(trade), [trade])
+ const priceImpactSeverity = warningSeverity(priceImpactWithoutFee)
+
+ const theme = useContext(ThemeContext)
+
+ return (
+
+
+
+
+
+ {trade.inputAmount.toSignificant(6)}
+
+
+
+
+ {trade.inputAmount.currency.symbol}
+
+
+
+
+
+
+
+
+
+ 2
+ ? theme.red1
+ : showAcceptChanges && trade.tradeType === TradeType.EXACT_INPUT
+ ? theme.primary1
+ : ''
+ }
+ >
+ {trade.outputAmount.toSignificant(6)}
+
+
+
+
+ {trade.outputAmount.currency.symbol}
+
+
+
+ {showAcceptChanges ? (
+
+
+
+
+ Price Updated
+
+
+ Accept
+
+
+
+ ) : null}
+ {!(trade instanceof MoolaDirectTrade) && (
+
+ {trade.tradeType === TradeType.EXACT_INPUT ? (
+
+ {`Output is estimated. You will receive at least `}
+
+ {slippageAdjustedAmounts[Field.OUTPUT]?.toSignificant(6)} {trade.outputAmount.currency.symbol}
+
+ {' or the transaction will revert.'}
+
+ ) : (
+
+ {`Input is estimated. You will sell at most `}
+
+ {slippageAdjustedAmounts[Field.INPUT]?.toSignificant(6)} {trade.inputAmount.currency.symbol}
+
+ {' or the transaction will revert.'}
+
+ )}
+
+ )}
+ {recipient !== null ? (
+
+
+ Output will be sent to{' '}
+ {isAddress(recipient) ? shortenAddress(recipient) : recipient}
+
+
+ ) : null}
+
+ )
+}
diff --git a/src/components/swap/SwapHeader.tsx b/src/components/swap/SwapHeader.tsx
index 6423eed8..f418ac53 100644
--- a/src/components/swap/SwapHeader.tsx
+++ b/src/components/swap/SwapHeader.tsx
@@ -23,7 +23,9 @@ export default function SwapHeader({
return (
- {title}
+
+ {title}
+
{hideSettings || }
diff --git a/src/components/swap/SwapModalFooter.tsx b/src/components/swap/SwapModalFooter.tsx
index fd291d84..86e7b9fe 100644
--- a/src/components/swap/SwapModalFooter.tsx
+++ b/src/components/swap/SwapModalFooter.tsx
@@ -61,6 +61,33 @@ export default function SwapModalFooter({
)
+ } else if (routingMethod === RoutingMethod.LIMIT) {
+ info = (
+
+
+
+ Price
+
+
+ {formatExecutionPrice(trade, showInverted)}
+ setShowInverted(!showInverted)}>
+
+
+
+
+
+ )
} else {
info = (
diff --git a/src/components/swap/TradePrice.tsx b/src/components/swap/TradePrice.tsx
index 1d5a768b..fd37a29d 100644
--- a/src/components/swap/TradePrice.tsx
+++ b/src/components/swap/TradePrice.tsx
@@ -1,4 +1,4 @@
-import { Price } from '@ubeswap/sdk'
+import { JSBI, Price } from '@ubeswap/sdk'
import React, { useContext } from 'react'
import { Repeat } from 'react-feather'
import { Text } from 'rebass'
@@ -15,7 +15,18 @@ interface TradePriceProps {
export default function TradePrice({ price, showInverted, setShowInverted }: TradePriceProps) {
const theme = useContext(ThemeContext)
- const formattedPrice = showInverted ? price?.toSignificant(6) : price?.invert()?.toSignificant(6)
+ let formattedPrice
+ if (price) {
+ if (showInverted) {
+ if (!JSBI.equal(price.denominator, JSBI.BigInt(0))) {
+ formattedPrice = price.toSignificant(6)
+ }
+ } else {
+ if (!JSBI.equal(price.numerator, JSBI.BigInt(0))) {
+ formattedPrice = price.invert().toSignificant(6)
+ }
+ }
+ }
const show = Boolean(price?.baseCurrency && price?.quoteCurrency)
const label = showInverted
diff --git a/src/components/swap/routing/describeTrade.ts b/src/components/swap/routing/describeTrade.ts
index 288ab0ae..82145d58 100644
--- a/src/components/swap/routing/describeTrade.ts
+++ b/src/components/swap/routing/describeTrade.ts
@@ -7,6 +7,7 @@ export enum RoutingMethod {
UBESWAP = 0,
MOOLA = 1,
MOOLA_ROUTER = 2,
+ LIMIT = 3,
}
export const describeTrade = (
diff --git a/src/components/swap/routing/index.ts b/src/components/swap/routing/index.ts
index be9ec6b6..ecad9888 100644
--- a/src/components/swap/routing/index.ts
+++ b/src/components/swap/routing/index.ts
@@ -1,5 +1,5 @@
import { useContractKit, useGetConnectedSigner } from '@celo-tools/use-contractkit'
-import { Signer } from '@ethersproject/abstract-signer'
+import { JsonRpcSigner } from '@ethersproject/providers'
import { ChainId, Trade } from '@ubeswap/sdk'
import { BigNumber, BigNumberish, CallOverrides, Contract, ContractTransaction, PayableOverrides } from 'ethers'
import { useCallback } from 'react'
@@ -10,7 +10,7 @@ type Head = Required extends [...infer H, any] ? H : never
type Last> = Required extends [...unknown[], infer L] ? L : never
type MethodArgs = Head>
-type DoTransactionFn = <
+export type DoTransactionFn = <
C extends Contract,
M extends string & keyof C['estimateGas'],
O extends Last> & (PayableOverrides | CallOverrides)
@@ -29,7 +29,18 @@ type DoTransactionFn = <
export interface TradeExecutor {
(args: {
trade: T
- signer: Signer
+ signer: JsonRpcSigner
+ chainId: ChainId.MAINNET | ChainId.ALFAJORES
+ doTransaction: DoTransactionFn
+ }): Promise<{
+ hash: string
+ }>
+}
+
+export interface CancelLimitOrderExecutor {
+ (args: {
+ orderHash: string
+ signer: JsonRpcSigner
chainId: ChainId.MAINNET | ChainId.ALFAJORES
doTransaction: DoTransactionFn
}): Promise<{
diff --git a/src/components/swap/routing/limit/queueLimitOrderTrade.ts b/src/components/swap/routing/limit/queueLimitOrderTrade.ts
new file mode 100644
index 00000000..0179f3f5
--- /dev/null
+++ b/src/components/swap/routing/limit/queueLimitOrderTrade.ts
@@ -0,0 +1,91 @@
+import { useGetConnectedSigner } from '@celo-tools/use-contractkit'
+import { ChainId, TokenAmount } from '@ubeswap/sdk'
+import { LimitOrderProtocol__factory } from 'generated/factories/LimitOrderProtocol__factory'
+import { OrderBook__factory } from 'generated/factories/OrderBook__factory'
+import { useCallback, useState } from 'react'
+import { buildOrderData } from 'utils/limitOrder'
+
+import {
+ LIMIT_ORDER_ADDRESS,
+ ORDER_BOOK_ADDRESS,
+ ORDER_BOOK_REWARD_DISTRIBUTOR_ADDRESS,
+ ZERO_ADDRESS,
+} from '../../../../constants'
+import { useDoTransaction } from '..'
+
+function cutLastArg(data: string, padding = 0) {
+ return data.substr(0, data.length - 64 - padding)
+}
+
+/**
+ * Queues a limit order trade.
+ * @returns
+ */
+export const useQueueLimitOrderTrade = () => {
+ const getConnectedSigner = useGetConnectedSigner()
+ const doTransaction = useDoTransaction()
+ const [loading, setLoading] = useState(false)
+ const queueLimitOrderCallback = useCallback(
+ async ({
+ inputAmount,
+ outputAmount,
+ chainId,
+ }: {
+ inputAmount: TokenAmount
+ outputAmount: TokenAmount
+ chainId: ChainId
+ }) => {
+ const signer = await getConnectedSigner()
+ const limitOrderAddr = LIMIT_ORDER_ADDRESS[chainId]
+ const orderBookAddr = ORDER_BOOK_ADDRESS[chainId]
+ const rewardDistributorAddr = ORDER_BOOK_REWARD_DISTRIBUTOR_ADDRESS[chainId]
+
+ const limitOrderProtocolIface = LimitOrderProtocol__factory.createInterface()
+ const orderBook = OrderBook__factory.connect(orderBookAddr, signer)
+
+ const makingAmount = inputAmount.raw.toString()
+ const takingAmount = outputAmount.raw.toString()
+
+ const limitOrder = {
+ salt: Math.floor(Math.random() * 1_000_000_000), // Reasonably random
+ makerAsset: inputAmount.currency.address,
+ takerAsset: outputAmount.currency.address,
+ maker: await signer.getAddress(),
+ receiver: ZERO_ADDRESS,
+ allowedSender: ZERO_ADDRESS,
+ makingAmount,
+ takingAmount,
+ makerAssetData: '0x',
+ takerAssetData: '0x',
+ getMakerAmount: cutLastArg(
+ limitOrderProtocolIface.encodeFunctionData('getMakerAmount', [makingAmount, takingAmount, 0])
+ ),
+ getTakerAmount: cutLastArg(
+ limitOrderProtocolIface.encodeFunctionData('getTakerAmount', [makingAmount, takingAmount, 0])
+ ),
+ predicate: '0x',
+ permit: '0x',
+ interaction: '0x',
+ }
+ try {
+ setLoading(true)
+ const limitOrderTypedData = buildOrderData(chainId.toString(), limitOrderAddr, limitOrder)
+ const limitOrderSignature = await signer._signTypedData(
+ limitOrderTypedData.domain,
+ limitOrderTypedData.types,
+ limitOrder
+ )
+ await doTransaction(orderBook, 'broadcastOrder', {
+ args: [limitOrder, limitOrderSignature, rewardDistributorAddr],
+ summary: `Place limit order for ${outputAmount.toSignificant(2)} ${outputAmount.currency.symbol}`,
+ })
+ } catch (e) {
+ console.error(e)
+ } finally {
+ setLoading(false)
+ }
+ },
+ [doTransaction, getConnectedSigner]
+ )
+ return { queueLimitOrderCallback, loading }
+}
diff --git a/src/components/swap/routing/moola/useMoola.ts b/src/components/swap/routing/moola/useMoola.ts
index 3976506f..3ef832c9 100644
--- a/src/components/swap/routing/moola/useMoola.ts
+++ b/src/components/swap/routing/moola/useMoola.ts
@@ -1,7 +1,7 @@
import { CeloContract } from '@celo/contractkit'
import { useContractKit, useProvider } from '@celo-tools/use-contractkit'
-import { CELO, ChainId, currencyEquals, cUSD, Token } from '@ubeswap/sdk'
-import { CEUR, MCELO, MCEUR, MCUSD } from 'constants/index'
+import { CELO, ChainId, cREAL, currencyEquals, cUSD, Token } from '@ubeswap/sdk'
+import { CEUR, MCELO, MCEUR, MCREAL, MCUSD } from 'constants/index'
import { useMemo } from 'react'
import { LendingPool, LendingPool__factory } from '../../../../generated'
@@ -15,6 +15,7 @@ export const moolaLendingPools = {
[CeloContract.GoldToken]: CELO[ChainId.ALFAJORES],
[CeloContract.StableToken]: cUSD[ChainId.ALFAJORES],
mcUSD: MCUSD[ChainId.ALFAJORES],
+ mCREAL: MCREAL[ChainId.ALFAJORES],
mCELO: MCELO[ChainId.ALFAJORES],
},
[ChainId.MAINNET]: {
@@ -24,12 +25,14 @@ export const moolaLendingPools = {
[CeloContract.GoldToken]: CELO[ChainId.MAINNET],
[CeloContract.StableToken]: cUSD[ChainId.MAINNET],
mcUSD: MCUSD[ChainId.MAINNET],
+ mCREAL: MCREAL[ChainId.MAINNET],
mCELO: MCELO[ChainId.MAINNET],
},
}
export const moolaDuals = (
[
+ [MCREAL, cREAL],
[MCUSD, cUSD],
[MCELO, CELO],
[MCEUR, CEUR],
diff --git a/src/constants/abis/limit/LimitOrderProtocol.json b/src/constants/abis/limit/LimitOrderProtocol.json
new file mode 100644
index 00000000..8675dc22
--- /dev/null
+++ b/src/constants/abis/limit/LimitOrderProtocol.json
@@ -0,0 +1,1726 @@
+[
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "newNonce",
+ "type": "uint256"
+ }
+ ],
+ "name": "NonceIncreased",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "bytes32",
+ "name": "orderHash",
+ "type": "bytes32"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "remainingRaw",
+ "type": "uint256"
+ }
+ ],
+ "name": "OrderCanceled",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "bytes32",
+ "name": "orderHash",
+ "type": "bytes32"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "remaining",
+ "type": "uint256"
+ }
+ ],
+ "name": "OrderFilled",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "bytes32",
+ "name": "orderHash",
+ "type": "bytes32"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ }
+ ],
+ "name": "OrderFilledRFQ",
+ "type": "event"
+ },
+ {
+ "inputs": [],
+ "name": "DOMAIN_SEPARATOR",
+ "outputs": [
+ {
+ "internalType": "bytes32",
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "LIMIT_ORDER_RFQ_TYPEHASH",
+ "outputs": [
+ {
+ "internalType": "bytes32",
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "LIMIT_ORDER_TYPEHASH",
+ "outputs": [
+ {
+ "internalType": "bytes32",
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint8",
+ "name": "amount",
+ "type": "uint8"
+ }
+ ],
+ "name": "advanceNonce",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address[]",
+ "name": "targets",
+ "type": "address[]"
+ },
+ {
+ "internalType": "bytes[]",
+ "name": "data",
+ "type": "bytes[]"
+ }
+ ],
+ "name": "and",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "target",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes",
+ "name": "data",
+ "type": "bytes"
+ }
+ ],
+ "name": "arbitraryStaticCall",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "salt",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "makerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "takerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "allowedSender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes",
+ "name": "makerAssetData",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "takerAssetData",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "getMakerAmount",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "getTakerAmount",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "predicate",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "permit",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "interaction",
+ "type": "bytes"
+ }
+ ],
+ "internalType": "struct OrderMixin.Order",
+ "name": "order",
+ "type": "tuple"
+ }
+ ],
+ "name": "cancelOrder",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "orderInfo",
+ "type": "uint256"
+ }
+ ],
+ "name": "cancelOrderRFQ",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "salt",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "makerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "takerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "allowedSender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes",
+ "name": "makerAssetData",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "takerAssetData",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "getMakerAmount",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "getTakerAmount",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "predicate",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "permit",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "interaction",
+ "type": "bytes"
+ }
+ ],
+ "internalType": "struct OrderMixin.Order",
+ "name": "order",
+ "type": "tuple"
+ }
+ ],
+ "name": "checkPredicate",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "contract AggregatorV3Interface",
+ "name": "oracle1",
+ "type": "address"
+ },
+ {
+ "internalType": "contract AggregatorV3Interface",
+ "name": "oracle2",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "spread",
+ "type": "uint256"
+ },
+ {
+ "internalType": "int256",
+ "name": "decimalsScale",
+ "type": "int256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "doublePrice",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "target",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes",
+ "name": "data",
+ "type": "bytes"
+ }
+ ],
+ "name": "eq",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "salt",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "makerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "takerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "allowedSender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes",
+ "name": "makerAssetData",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "takerAssetData",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "getMakerAmount",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "getTakerAmount",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "predicate",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "permit",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "interaction",
+ "type": "bytes"
+ }
+ ],
+ "internalType": "struct OrderMixin.Order",
+ "name": "order",
+ "type": "tuple"
+ },
+ {
+ "internalType": "bytes",
+ "name": "signature",
+ "type": "bytes"
+ },
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "thresholdAmount",
+ "type": "uint256"
+ }
+ ],
+ "internalType": "struct OrderMixin.OrderAmounts",
+ "name": "orderAmounts",
+ "type": "tuple"
+ }
+ ],
+ "name": "fillOrder",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "info",
+ "type": "uint256"
+ },
+ {
+ "internalType": "contract IERC20",
+ "name": "makerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "contract IERC20",
+ "name": "takerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "allowedSender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ }
+ ],
+ "internalType": "struct OrderRFQMixin.OrderRFQ",
+ "name": "order",
+ "type": "tuple"
+ },
+ {
+ "internalType": "bytes",
+ "name": "signature",
+ "type": "bytes"
+ },
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ }
+ ],
+ "name": "fillOrderRFQ",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "info",
+ "type": "uint256"
+ },
+ {
+ "internalType": "contract IERC20",
+ "name": "makerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "contract IERC20",
+ "name": "takerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "allowedSender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ }
+ ],
+ "internalType": "struct OrderRFQMixin.OrderRFQ",
+ "name": "order",
+ "type": "tuple"
+ },
+ {
+ "internalType": "bytes",
+ "name": "signature",
+ "type": "bytes"
+ },
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "target",
+ "type": "address"
+ }
+ ],
+ "name": "fillOrderRFQTo",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "info",
+ "type": "uint256"
+ },
+ {
+ "internalType": "contract IERC20",
+ "name": "makerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "contract IERC20",
+ "name": "takerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "allowedSender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ }
+ ],
+ "internalType": "struct OrderRFQMixin.OrderRFQ",
+ "name": "order",
+ "type": "tuple"
+ },
+ {
+ "internalType": "bytes",
+ "name": "signature",
+ "type": "bytes"
+ },
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "target",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes",
+ "name": "permit",
+ "type": "bytes"
+ }
+ ],
+ "name": "fillOrderRFQToWithPermit",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "salt",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "makerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "takerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "allowedSender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes",
+ "name": "makerAssetData",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "takerAssetData",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "getMakerAmount",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "getTakerAmount",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "predicate",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "permit",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "interaction",
+ "type": "bytes"
+ }
+ ],
+ "internalType": "struct OrderMixin.Order",
+ "name": "order",
+ "type": "tuple"
+ },
+ {
+ "internalType": "bytes",
+ "name": "signature",
+ "type": "bytes"
+ },
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "thresholdAmount",
+ "type": "uint256"
+ }
+ ],
+ "internalType": "struct OrderMixin.OrderAmounts",
+ "name": "orderAmounts",
+ "type": "tuple"
+ },
+ {
+ "internalType": "address",
+ "name": "target",
+ "type": "address"
+ }
+ ],
+ "name": "fillOrderTo",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "salt",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "makerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "takerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "allowedSender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes",
+ "name": "makerAssetData",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "takerAssetData",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "getMakerAmount",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "getTakerAmount",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "predicate",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "permit",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "interaction",
+ "type": "bytes"
+ }
+ ],
+ "internalType": "struct OrderMixin.Order",
+ "name": "order",
+ "type": "tuple"
+ },
+ {
+ "internalType": "bytes",
+ "name": "signature",
+ "type": "bytes"
+ },
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "thresholdAmount",
+ "type": "uint256"
+ }
+ ],
+ "internalType": "struct OrderMixin.OrderAmounts",
+ "name": "orderAmounts",
+ "type": "tuple"
+ },
+ {
+ "internalType": "address",
+ "name": "target",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes",
+ "name": "extraInteraction",
+ "type": "bytes"
+ }
+ ],
+ "name": "fillOrderToWithExtraInteraction",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "salt",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "makerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "takerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "allowedSender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes",
+ "name": "makerAssetData",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "takerAssetData",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "getMakerAmount",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "getTakerAmount",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "predicate",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "permit",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "interaction",
+ "type": "bytes"
+ }
+ ],
+ "internalType": "struct OrderMixin.Order",
+ "name": "order",
+ "type": "tuple"
+ },
+ {
+ "internalType": "bytes",
+ "name": "signature",
+ "type": "bytes"
+ },
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "thresholdAmount",
+ "type": "uint256"
+ }
+ ],
+ "internalType": "struct OrderMixin.OrderAmounts",
+ "name": "orderAmounts",
+ "type": "tuple"
+ },
+ {
+ "internalType": "address",
+ "name": "target",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes",
+ "name": "permit",
+ "type": "bytes"
+ }
+ ],
+ "name": "fillOrderToWithPermit",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "orderMakerAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "orderTakerAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "swapTakerAmount",
+ "type": "uint256"
+ }
+ ],
+ "name": "getMakerAmount",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "pure",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "orderMakerAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "orderTakerAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "swapMakerAmount",
+ "type": "uint256"
+ }
+ ],
+ "name": "getTakerAmount",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "pure",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "target",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes",
+ "name": "data",
+ "type": "bytes"
+ }
+ ],
+ "name": "gt",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "salt",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "makerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "takerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "allowedSender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes",
+ "name": "makerAssetData",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "takerAssetData",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "getMakerAmount",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "getTakerAmount",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "predicate",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "permit",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "interaction",
+ "type": "bytes"
+ }
+ ],
+ "internalType": "struct OrderMixin.Order",
+ "name": "order",
+ "type": "tuple"
+ }
+ ],
+ "name": "hashOrder",
+ "outputs": [
+ {
+ "internalType": "bytes32",
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "info",
+ "type": "uint256"
+ },
+ {
+ "internalType": "contract IERC20",
+ "name": "makerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "contract IERC20",
+ "name": "takerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "allowedSender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ }
+ ],
+ "internalType": "struct OrderRFQMixin.OrderRFQ",
+ "name": "order",
+ "type": "tuple"
+ }
+ ],
+ "name": "hashOrderRFQ",
+ "outputs": [
+ {
+ "internalType": "bytes32",
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "increaseNonce",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "slot",
+ "type": "uint256"
+ }
+ ],
+ "name": "invalidatorForOrderRFQ",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "target",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes",
+ "name": "data",
+ "type": "bytes"
+ }
+ ],
+ "name": "lt",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "nonce",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "makerAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "makerNonce",
+ "type": "uint256"
+ }
+ ],
+ "name": "nonceEquals",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address[]",
+ "name": "targets",
+ "type": "address[]"
+ },
+ {
+ "internalType": "bytes[]",
+ "name": "data",
+ "type": "bytes[]"
+ }
+ ],
+ "name": "or",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "bytes32",
+ "name": "orderHash",
+ "type": "bytes32"
+ }
+ ],
+ "name": "remaining",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "bytes32",
+ "name": "orderHash",
+ "type": "bytes32"
+ }
+ ],
+ "name": "remainingRaw",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "bytes32[]",
+ "name": "orderHashes",
+ "type": "bytes32[]"
+ }
+ ],
+ "name": "remainingsRaw",
+ "outputs": [
+ {
+ "internalType": "uint256[]",
+ "name": "",
+ "type": "uint256[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address[]",
+ "name": "targets",
+ "type": "address[]"
+ },
+ {
+ "internalType": "bytes[]",
+ "name": "data",
+ "type": "bytes[]"
+ }
+ ],
+ "name": "simulateCalls",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "contract AggregatorV3Interface",
+ "name": "oracle",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "inverseAndSpread",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "singlePrice",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "time",
+ "type": "uint256"
+ }
+ ],
+ "name": "timestampBelow",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ }
+]
diff --git a/src/constants/abis/limit/OrderBook.json b/src/constants/abis/limit/OrderBook.json
new file mode 100644
index 00000000..46e70520
--- /dev/null
+++ b/src/constants/abis/limit/OrderBook.json
@@ -0,0 +1,412 @@
+[
+ {
+ "inputs": [
+ {
+ "internalType": "contract LimitOrderProtocol",
+ "name": "_limitOrderProtocol",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_fee",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "_feeRecipient",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "constructor"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "oldFee",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "newFee",
+ "type": "uint256"
+ }
+ ],
+ "name": "FeeChanged",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "address",
+ "name": "oldFeeRecipient",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "address",
+ "name": "newFeeRecipient",
+ "type": "address"
+ }
+ ],
+ "name": "FeeRecipientChanged",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "bytes32",
+ "name": "orderHash",
+ "type": "bytes32"
+ },
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "salt",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "makerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "takerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "allowedSender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes",
+ "name": "makerAssetData",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "takerAssetData",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "getMakerAmount",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "getTakerAmount",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "predicate",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "permit",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "interaction",
+ "type": "bytes"
+ }
+ ],
+ "indexed": false,
+ "internalType": "struct OrderMixin.Order",
+ "name": "order",
+ "type": "tuple"
+ },
+ {
+ "indexed": false,
+ "internalType": "bytes",
+ "name": "signature",
+ "type": "bytes"
+ }
+ ],
+ "name": "OrderBroadcasted",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "previousOwner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "OwnershipTransferred",
+ "type": "event"
+ },
+ {
+ "inputs": [],
+ "name": "MAX_FEE",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "PCT_DENOMINATOR",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "salt",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "makerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "takerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "allowedSender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes",
+ "name": "makerAssetData",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "takerAssetData",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "getMakerAmount",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "getTakerAmount",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "predicate",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "permit",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "interaction",
+ "type": "bytes"
+ }
+ ],
+ "internalType": "struct OrderMixin.Order",
+ "name": "_order",
+ "type": "tuple"
+ },
+ {
+ "internalType": "bytes",
+ "name": "_signature",
+ "type": "bytes"
+ },
+ {
+ "internalType": "address",
+ "name": "_notificationTarget",
+ "type": "address"
+ }
+ ],
+ "name": "broadcastOrder",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "_fee",
+ "type": "uint256"
+ }
+ ],
+ "name": "changeFee",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_feeRecipient",
+ "type": "address"
+ }
+ ],
+ "name": "changeFeeRecipient",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "fee",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "feeRecipient",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "limitOrderProtocol",
+ "outputs": [
+ {
+ "internalType": "contract LimitOrderProtocol",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "owner",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "renounceOwnership",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "transferOwnership",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ }
+]
diff --git a/src/constants/abis/limit/OrderBookRewardDistributor.json b/src/constants/abis/limit/OrderBookRewardDistributor.json
new file mode 100644
index 00000000..ea25bcdc
--- /dev/null
+++ b/src/constants/abis/limit/OrderBookRewardDistributor.json
@@ -0,0 +1,381 @@
+[
+ {
+ "inputs": [
+ {
+ "internalType": "contract IERC20",
+ "name": "_rewardCurrency",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "constructor"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "address",
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "ERC20Rescued",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "previousOwner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "OwnershipTransferred",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "address",
+ "name": "oldRewardCurrency",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "address",
+ "name": "newRewardCurrency",
+ "type": "address"
+ }
+ ],
+ "name": "RewardCurrencyChanged",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "address",
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "oldRewardRate",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "newRewardRate",
+ "type": "uint256"
+ }
+ ],
+ "name": "RewardRateChanged",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "address",
+ "name": "user",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "bool",
+ "name": "whitelisted",
+ "type": "bool"
+ }
+ ],
+ "name": "WhitelistChanged",
+ "type": "event"
+ },
+ {
+ "inputs": [],
+ "name": "PCT_DENOMINATOR",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_user",
+ "type": "address"
+ }
+ ],
+ "name": "addToWhitelist",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "contract IERC20",
+ "name": "_rewardCurrency",
+ "type": "address"
+ }
+ ],
+ "name": "changeRewardCurrency",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_token",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_rewardRate",
+ "type": "uint256"
+ }
+ ],
+ "name": "changeRewardRate",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "salt",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "makerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "takerAsset",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "allowedSender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "makingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "takingAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes",
+ "name": "makerAssetData",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "takerAssetData",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "getMakerAmount",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "getTakerAmount",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "predicate",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "permit",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "interaction",
+ "type": "bytes"
+ }
+ ],
+ "internalType": "struct OrderMixin.Order",
+ "name": "_order",
+ "type": "tuple"
+ },
+ {
+ "internalType": "address",
+ "name": "_rewardRecipient",
+ "type": "address"
+ }
+ ],
+ "name": "notifyOrderBroadcasted",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "owner",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_user",
+ "type": "address"
+ }
+ ],
+ "name": "removeFromWhitelist",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "renounceOwnership",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "contract IERC20",
+ "name": "_token",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "rescueERC20",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "rewardCurrency",
+ "outputs": [
+ {
+ "internalType": "contract IERC20",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "rewardRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "transferOwnership",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "whitelist",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ }
+]
diff --git a/src/constants/abis/moola/moola-staking-rewards.ts b/src/constants/abis/moola/moola-staking-rewards.ts
new file mode 100644
index 00000000..28f7252b
--- /dev/null
+++ b/src/constants/abis/moola/moola-staking-rewards.ts
@@ -0,0 +1,5 @@
+import { Interface } from '@ethersproject/abi'
+
+import MOOLA_STAKING_REWARDS_ABI from './MoolaStakingRewards.json'
+
+export const MOOLA_STAKING_REWARDS_INTERFACE = new Interface(MOOLA_STAKING_REWARDS_ABI)
diff --git a/src/constants/abis/multicall/Multicall.json b/src/constants/abis/multicall/Multicall.json
new file mode 100644
index 00000000..76cadf4c
--- /dev/null
+++ b/src/constants/abis/multicall/Multicall.json
@@ -0,0 +1,165 @@
+[
+ {
+ "inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "target", "type": "address" },
+ { "internalType": "bytes", "name": "callData", "type": "bytes" }
+ ],
+ "internalType": "struct Multicall2.Call[]",
+ "name": "calls",
+ "type": "tuple[]"
+ }
+ ],
+ "name": "aggregate",
+ "outputs": [
+ { "internalType": "uint256", "name": "blockNumber", "type": "uint256" },
+ { "internalType": "bytes[]", "name": "returnData", "type": "bytes[]" }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "target", "type": "address" },
+ { "internalType": "bytes", "name": "callData", "type": "bytes" }
+ ],
+ "internalType": "struct Multicall2.Call[]",
+ "name": "calls",
+ "type": "tuple[]"
+ }
+ ],
+ "name": "blockAndAggregate",
+ "outputs": [
+ { "internalType": "uint256", "name": "blockNumber", "type": "uint256" },
+ { "internalType": "bytes32", "name": "blockHash", "type": "bytes32" },
+ {
+ "components": [
+ { "internalType": "bool", "name": "success", "type": "bool" },
+ { "internalType": "bytes", "name": "returnData", "type": "bytes" }
+ ],
+ "internalType": "struct Multicall2.Result[]",
+ "name": "returnData",
+ "type": "tuple[]"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" }],
+ "name": "getBlockHash",
+ "outputs": [{ "internalType": "bytes32", "name": "blockHash", "type": "bytes32" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "getBlockNumber",
+ "outputs": [{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "getCurrentBlockCoinbase",
+ "outputs": [{ "internalType": "address", "name": "coinbase", "type": "address" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "getCurrentBlockDifficulty",
+ "outputs": [{ "internalType": "uint256", "name": "difficulty", "type": "uint256" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "getCurrentBlockGasLimit",
+ "outputs": [{ "internalType": "uint256", "name": "gaslimit", "type": "uint256" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "getCurrentBlockTimestamp",
+ "outputs": [{ "internalType": "uint256", "name": "timestamp", "type": "uint256" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [{ "internalType": "address", "name": "addr", "type": "address" }],
+ "name": "getEthBalance",
+ "outputs": [{ "internalType": "uint256", "name": "balance", "type": "uint256" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "getLastBlockHash",
+ "outputs": [{ "internalType": "bytes32", "name": "blockHash", "type": "bytes32" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "bool", "name": "requireSuccess", "type": "bool" },
+ {
+ "components": [
+ { "internalType": "address", "name": "target", "type": "address" },
+ { "internalType": "bytes", "name": "callData", "type": "bytes" }
+ ],
+ "internalType": "struct Multicall2.Call[]",
+ "name": "calls",
+ "type": "tuple[]"
+ }
+ ],
+ "name": "tryAggregate",
+ "outputs": [
+ {
+ "components": [
+ { "internalType": "bool", "name": "success", "type": "bool" },
+ { "internalType": "bytes", "name": "returnData", "type": "bytes" }
+ ],
+ "internalType": "struct Multicall2.Result[]",
+ "name": "returnData",
+ "type": "tuple[]"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "bool", "name": "requireSuccess", "type": "bool" },
+ {
+ "components": [
+ { "internalType": "address", "name": "target", "type": "address" },
+ { "internalType": "bytes", "name": "callData", "type": "bytes" }
+ ],
+ "internalType": "struct Multicall2.Call[]",
+ "name": "calls",
+ "type": "tuple[]"
+ }
+ ],
+ "name": "tryBlockAndAggregate",
+ "outputs": [
+ { "internalType": "uint256", "name": "blockNumber", "type": "uint256" },
+ { "internalType": "bytes32", "name": "blockHash", "type": "bytes32" },
+ {
+ "components": [
+ { "internalType": "bool", "name": "success", "type": "bool" },
+ { "internalType": "bytes", "name": "returnData", "type": "bytes" }
+ ],
+ "internalType": "struct Multicall2.Result[]",
+ "name": "returnData",
+ "type": "tuple[]"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ }
+]
diff --git a/src/constants/index.ts b/src/constants/index.ts
index f99e29a4..4b1deaed 100644
--- a/src/constants/index.ts
+++ b/src/constants/index.ts
@@ -21,11 +21,52 @@ export const AVERAGE_BLOCK_TIME_IN_SECS = 13
export const PROPOSAL_LENGTH_IN_BLOCKS = 40_320
export const PROPOSAL_LENGTH_IN_SECS = AVERAGE_BLOCK_TIME_IN_SECS * PROPOSAL_LENGTH_IN_BLOCKS
+export const LIMIT_ORDER_ADDRESS = {
+ [ChainId.MAINNET]: '0x83013dCE53676F523dB8175832f2f3AD5B1fBb1f',
+ [ChainId.ALFAJORES]: '0xb5911e904EEf100803D5d4bDb22ff1177324e7F3',
+ [ChainId.BAKLAVA]: '',
+}
+
+export const ORDER_BOOK_ADDRESS = {
+ [ChainId.MAINNET]: '0xF3acED685402F5Ece86A0672512a95Ab20a8f1bC',
+ [ChainId.ALFAJORES]: '0x6F15Ed13b9a5ce8C3B19861331458556374628Ad',
+ [ChainId.BAKLAVA]: '',
+}
+
+export const ORDER_BOOK_REWARD_DISTRIBUTOR_ADDRESS = {
+ [ChainId.MAINNET]: '0xEEc60eB73DfC052682351E0D4309797B3C9eB6d8',
+ [ChainId.ALFAJORES]: '0x08AD74C7D04c1e54E96A4142598BcCe56A4a4119',
+ [ChainId.BAKLAVA]: '',
+}
+
+export const MULTICALL_ADDRESS = {
+ [ChainId.MAINNET]: '0x75f59534dd892c1f8a7b172d639fa854d529ada3',
+ [ChainId.ALFAJORES]: '0x387ce7960b5DA5381De08Ea4967b13a7c8cAB3f6',
+ [ChainId.BAKLAVA]: '',
+}
+
export const POOF = {
[ChainId.MAINNET]: new Token(ChainId.MAINNET, '0x00400FcbF0816bebB94654259de7273f4A05c762', 18, 'POOF', 'POOF'),
[ChainId.ALFAJORES]: new Token(ChainId.ALFAJORES, '0x00400FcbF0816bebB94654259de7273f4A05c762', 18, 'POOF', 'POOF'),
}
+export const MCREAL = {
+ [ChainId.MAINNET]: new Token(
+ ChainId.MAINNET,
+ '0x9802d866fdE4563d088a6619F7CeF82C0B991A55',
+ 18,
+ 'mCREAL',
+ 'Moola cREAL'
+ ),
+ [ChainId.ALFAJORES]: new Token(
+ ChainId.ALFAJORES,
+ '0x3D0EDA535ca4b15c739D46761d24E42e37664Ad7',
+ 18,
+ 'mCREAL',
+ 'Moola cREAL'
+ ),
+}
+
export const MCUSD = {
[ChainId.MAINNET]: new Token(
ChainId.MAINNET,
diff --git a/src/hooks/useContract.ts b/src/hooks/useContract.ts
index 703abc02..6c7fa9e5 100644
--- a/src/hooks/useContract.ts
+++ b/src/hooks/useContract.ts
@@ -7,13 +7,24 @@ import { StakingInfo } from 'state/stake/hooks'
import ENS_PUBLIC_RESOLVER_ABI from '../constants/abis/ens-public-resolver.json'
import ERC20_ABI, { ERC20_BYTES32_ABI } from '../constants/abis/erc20'
+import LIMIT_ORDER_PROTOCOL_ABI from '../constants/abis/limit/LimitOrderProtocol.json'
+import ORDER_BOOK_ABI from '../constants/abis/limit/OrderBook.json'
+import ORDER_BOOK_REWARD_DISTRUBUTOR_ABI from '../constants/abis/limit/OrderBookRewardDistributor.json'
import DUAL_REWARDS_ABI from '../constants/abis/moola/MoolaStakingRewards.json'
import POOL_MANAGER_ABI from '../constants/abis/pool-manager.json'
import RELEASE_UBE_ABI from '../constants/abis/ReleaseUbe.json'
import STAKING_REWARDS_ABI from '../constants/abis/StakingRewards.json'
import VOTABLE_STAKING_REWARDS_ABI from '../constants/abis/VotableStakingRewards.json'
import { MULTICALL_ABI, MULTICALL_NETWORKS } from '../constants/multicall'
-import { Erc20, MoolaStakingRewards, PoolManager, StakingRewards } from '../generated'
+import {
+ Erc20,
+ LimitOrderProtocol,
+ MoolaStakingRewards,
+ OrderBook,
+ OrderBookRewardDistributor,
+ PoolManager,
+ StakingRewards,
+} from '../generated'
import { getContract } from '../utils'
// returns null on errors
@@ -119,3 +130,25 @@ export function useMultiStakingContract(
): MoolaStakingRewards | null {
return useContract(stakingAddress, DUAL_REWARDS_ABI, withSignerIfPossible) as MoolaStakingRewards | null
}
+
+export function useOrderBookContract(address?: string, withSignerIfPossible?: boolean): OrderBook | null {
+ return useContract(address, ORDER_BOOK_ABI, withSignerIfPossible) as OrderBook | null
+}
+
+export function useOrderBookRewardDistributorContract(
+ address?: string,
+ withSignerIfPossible?: boolean
+): OrderBookRewardDistributor | null {
+ return useContract(
+ address,
+ ORDER_BOOK_REWARD_DISTRUBUTOR_ABI,
+ withSignerIfPossible
+ ) as OrderBookRewardDistributor | null
+}
+
+export function useLimitOrderProtocolContract(
+ address?: string,
+ withSignerIfPossible?: boolean
+): LimitOrderProtocol | null {
+ return useContract(address, LIMIT_ORDER_PROTOCOL_ABI, withSignerIfPossible) as LimitOrderProtocol | null
+}
diff --git a/src/index.tsx b/src/index.tsx
index ca89c790..282e7d1e 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -113,6 +113,7 @@ function Updaters() {
ReactDOM.render(
+ {/* TODO: Mainnet, not alfajores */}
An unexpected error occured on this part of the page. Please reload.
}>
+
diff --git a/src/pages/Earn/useFarmRegistry.ts b/src/pages/Earn/useFarmRegistry.ts
index 375b09a8..fa7b245b 100644
--- a/src/pages/Earn/useFarmRegistry.ts
+++ b/src/pages/Earn/useFarmRegistry.ts
@@ -26,9 +26,9 @@ const blacklist: Record = {
}
const featuredPoolWhitelist: Record = {
- '0x6F11B6eA70DEe4f167b1A4ED1F01C903f6781960': true, // PACT
+ '0x6F11B6eA70DEe4f167b1A4ED1F01C903f6781960': false, // PACT
'0xEfe2f9d62E45815837b4f20c1F44F0A83605B540': false, // ARI
- '0x155DA6F164D925E3a91F510B50DEC08aA03B4071': true, // IMMO
+ '0x155DA6F164D925E3a91F510B50DEC08aA03B4071': false, // IMMO
}
const CREATION_BLOCK = 9840049
diff --git a/src/pages/LimitOrder/LimitOrderHistory.tsx b/src/pages/LimitOrder/LimitOrderHistory.tsx
new file mode 100644
index 00000000..06cb6183
--- /dev/null
+++ b/src/pages/LimitOrder/LimitOrderHistory.tsx
@@ -0,0 +1,61 @@
+import { useContractKit } from '@celo-tools/use-contractkit'
+import { ChainId as UbeswapChainId } from '@ubeswap/sdk'
+import { TabButton } from 'components/Button'
+import LimitOrderHistoryBody from 'components/LimitOrderHistory/LimitOrderHistoryBody'
+import LimitOrderHistoryItem from 'components/LimitOrderHistory/LimitOrderHistoryItem'
+import { Wrapper } from 'components/swap/styleds'
+import { useToken } from 'hooks/Tokens'
+import { useOrderBookRewardDistributorContract } from 'hooks/useContract'
+import React, { useState } from 'react'
+import { useSingleCallResult } from 'state/multicall/hooks'
+
+import { ORDER_BOOK_REWARD_DISTRIBUTOR_ADDRESS } from '../../constants'
+import { useLimitOrdersHistory } from './useOrderBroadcasted'
+
+export const LimitOrderHistory: React.FC = () => {
+ const { network } = useContractKit()
+ const chainId = network.chainId as unknown as UbeswapChainId
+ const limitOrderHistory = useLimitOrdersHistory()
+
+ const [openOrdersTabActive, setOpenOrdersTabActive] = useState(true)
+
+ const rewardDistributorContract = useOrderBookRewardDistributorContract(
+ ORDER_BOOK_REWARD_DISTRIBUTOR_ADDRESS[chainId]
+ )
+ const rewardCurrencyAddress = useSingleCallResult(rewardDistributorContract, 'rewardCurrency', []).result?.[0]
+ const rewardCurrency = useToken(rewardCurrencyAddress)
+
+ return (
+
+
+ setOpenOrdersTabActive(true)}>
+ Open ({limitOrderHistory.filter((limitOrderHist) => limitOrderHist.isOrderOpen).length})
+
+ setOpenOrdersTabActive(false)}>
+ Completed ({limitOrderHistory.filter((limitOrderHist) => !limitOrderHist.isOrderOpen).length})
+
+
+
+
+ {limitOrderHistory
+ .filter((limitOrderHist) => {
+ if (openOrdersTabActive) {
+ return limitOrderHist.isOrderOpen
+ }
+ return !limitOrderHist.isOrderOpen
+ })
+ .reverse()
+ .map((limitOrderHist, idx, arr) => {
+ return (
+
+ )
+ })}
+
+
+ )
+}
diff --git a/src/pages/LimitOrder/executCancelOrder.tsx b/src/pages/LimitOrder/executCancelOrder.tsx
new file mode 100644
index 00000000..e1d7daf1
--- /dev/null
+++ b/src/pages/LimitOrder/executCancelOrder.tsx
@@ -0,0 +1,31 @@
+import { CancelLimitOrderExecutor } from 'components/swap/routing'
+import { ContractTransaction } from 'ethers'
+import { LimitOrderProtocol__factory, OrderBook__factory } from 'generated'
+
+import { LIMIT_ORDER_ADDRESS, ORDER_BOOK_ADDRESS } from '../../constants'
+
+export const executeCancelOrder: CancelLimitOrderExecutor = async ({ signer, chainId, orderHash, doTransaction }) => {
+ const orderBookAddr = ORDER_BOOK_ADDRESS[chainId]
+ const limitOrderAddr = LIMIT_ORDER_ADDRESS[chainId]
+
+ const orderBook = OrderBook__factory.connect(orderBookAddr, signer)
+ const limitOrderProtocolFactory = LimitOrderProtocol__factory.connect(limitOrderAddr, signer)
+
+ const cancel = async (): Promise => {
+ const orders = await orderBook.queryFilter(orderBook.filters['OrderBroadcasted'](undefined, orderHash), 0, 'latest')
+ if (orders.length === 0) {
+ throw new Error('Error finding the order')
+ } else if (orders.length > 0) {
+ console.warn('More than one order was found with the same hash')
+ }
+
+ const { order } = orders[0].args
+
+ return await doTransaction(limitOrderProtocolFactory, 'cancelOrder', {
+ args: [order],
+ summary: `Cancel Order`,
+ })
+ }
+
+ return { hash: (await cancel()).hash }
+}
diff --git a/src/pages/LimitOrder/index.tsx b/src/pages/LimitOrder/index.tsx
new file mode 100644
index 00000000..4be1b762
--- /dev/null
+++ b/src/pages/LimitOrder/index.tsx
@@ -0,0 +1,479 @@
+import { useContractKit, WalletTypes } from '@celo-tools/use-contractkit'
+import { RampInstantSDK } from '@ramp-network/ramp-instant-sdk'
+import { ChainId as UbeswapChainId, cUSD, JSBI, TokenAmount, Trade } from '@ubeswap/sdk'
+import { CardNoise, CardSection, DataCard } from 'components/earn/styled'
+import { useQueueLimitOrderTrade } from 'components/swap/routing/limit/queueLimitOrderTrade'
+import { useTradeCallback } from 'components/swap/routing/useTradeCallback'
+import { useIsTransactionUnsupported } from 'hooks/Trades'
+import { useOrderBookContract, useOrderBookRewardDistributorContract } from 'hooks/useContract'
+import useENS from 'hooks/useENS'
+import React, { useCallback, useContext, useEffect, useState } from 'react'
+import ReactGA from 'react-ga'
+import { useTranslation } from 'react-i18next'
+import { Text } from 'rebass'
+import {
+ useDerivedLimitOrderInfo,
+ useLimitOrderActionHandlers,
+ useLimitOrderState,
+ useMarketPriceDiff,
+} from 'state/limit/hooks'
+import { useSingleCallResult } from 'state/multicall/hooks'
+import { ThemeContext } from 'styled-components'
+
+import { ButtonConfirmed, ButtonLight, ButtonPrimary, TabButton } from '../../components/Button'
+import Card from '../../components/Card'
+import Column, { AutoColumn, TopSectionLimitOrder } from '../../components/Column'
+import CurrencyInputPanel from '../../components/CurrencyInputPanel'
+import Loader from '../../components/Loader'
+import ProgressSteps from '../../components/ProgressSteps'
+import { AutoRow, RowBetween } from '../../components/Row'
+import ConfirmSwapModal from '../../components/swap/ConfirmSwapModal'
+import { BottomGrouping, Wrapper } from '../../components/swap/styleds'
+import SwapHeader from '../../components/swap/SwapHeader'
+import TradePrice from '../../components/swap/TradePrice'
+import { LIMIT_ORDER_ADDRESS, ORDER_BOOK_ADDRESS, ORDER_BOOK_REWARD_DISTRIBUTOR_ADDRESS } from '../../constants'
+import { useCurrency, useToken } from '../../hooks/Tokens'
+import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
+import { useWalletModalToggle } from '../../state/application/hooks'
+import { Field } from '../../state/limit/actions'
+import { useUserSlippageTolerance } from '../../state/user/hooks'
+import { TYPE } from '../../theme'
+import AppBody from '../AppBody'
+import { LimitOrderHistory } from './LimitOrderHistory'
+
+export const BPS_DENOMINATOR = JSBI.BigInt(1_000_000)
+
+export default function LimitOrder() {
+ const { address: account, network, walletType } = useContractKit()
+ const chainId = network.chainId as unknown as UbeswapChainId
+ const { queueLimitOrderCallback, loading: queueOrderLoading } = useQueueLimitOrderTrade()
+
+ const { t } = useTranslation()
+
+ const theme = useContext(ThemeContext)
+
+ // toggle wallet when disconnected
+ const toggleWalletModal = useWalletModalToggle()
+
+ // get custom setting values for user
+ const [allowedSlippage] = useUserSlippageTolerance()
+
+ const orderBookContract = useOrderBookContract(ORDER_BOOK_ADDRESS[chainId])
+ const orderBookFee = useSingleCallResult(orderBookContract, 'fee', []).result?.[0]
+
+ // swap state
+ const { tokenTypedValue, priceTypedValue, recipient } = useLimitOrderState()
+ const {
+ v2Trade: trade,
+ parsedInputTotal,
+ parsedOutputTotal,
+ currencies,
+ inputError: limitOrderInputError,
+ showRamp,
+ buying,
+ } = useDerivedLimitOrderInfo()
+ const { address: recipientAddress } = useENS(recipient)
+
+ const rewardDistributorContract = useOrderBookRewardDistributorContract(
+ ORDER_BOOK_REWARD_DISTRIBUTOR_ADDRESS[chainId]
+ )
+ const rewardCurrencyAddress = useSingleCallResult(rewardDistributorContract, 'rewardCurrency', []).result?.[0]
+ const rewardCurrency = useToken(rewardCurrencyAddress)
+ const rewardRate = useSingleCallResult(rewardDistributorContract, 'rewardRate', [
+ buying ? currencies?.PRICE?.address : currencies?.TOKEN?.address,
+ ]).result?.[0]
+
+ const { onCurrencySelection, onUserInput, setBuying } = useLimitOrderActionHandlers()
+ const defaultPriceCurrency = useCurrency(cUSD[chainId].address)
+ useEffect(() => {
+ defaultPriceCurrency && onCurrencySelection(Field.PRICE, defaultPriceCurrency)
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [])
+
+ const isValid = !limitOrderInputError
+
+ const handleTypeTokenAmount = useCallback(
+ (value: string) => {
+ onUserInput(Field.TOKEN, value)
+ },
+ [onUserInput]
+ )
+ const handleTypePrice = useCallback(
+ (value: string) => {
+ onUserInput(Field.PRICE, value)
+ },
+ [onUserInput]
+ )
+
+ // modal and loading
+ const [{ showConfirm, tradeToConfirm, swapErrorMessage, attemptingTxn, txHash }, setSwapState] = useState<{
+ showConfirm: boolean
+ tradeToConfirm: Trade | undefined
+ attemptingTxn: boolean
+ swapErrorMessage: string | undefined
+ txHash: string | undefined
+ }>({
+ showConfirm: false,
+ tradeToConfirm: undefined,
+ attemptingTxn: false,
+ swapErrorMessage: undefined,
+ txHash: undefined,
+ })
+
+ const formattedAmounts = {
+ [Field.PRICE]: priceTypedValue,
+ [Field.TOKEN]: tokenTypedValue,
+ }
+
+ // check whether the user has approved the router on the input token
+ // TODO: inputAmount may not work if it is the dependent field
+ const [limitOrderApproval, limitOrderApprovalCallback] = useApproveCallback(
+ parsedInputTotal,
+ LIMIT_ORDER_ADDRESS[chainId]
+ )
+ const orderFee =
+ parsedInputTotal && orderBookFee
+ ? new TokenAmount(
+ parsedInputTotal.currency,
+ JSBI.divide(JSBI.multiply(parsedInputTotal.raw, JSBI.BigInt(orderBookFee.toString())), BPS_DENOMINATOR)
+ )
+ : undefined
+
+ const { aboveMarketPrice, marketPriceDiffIndicator } = useMarketPriceDiff()
+
+ const reward =
+ parsedInputTotal && rewardCurrency && rewardRate
+ ? new TokenAmount(
+ rewardCurrency,
+ JSBI.divide(JSBI.multiply(parsedInputTotal.raw, JSBI.BigInt(rewardRate.toString())), BPS_DENOMINATOR)
+ )
+ : undefined
+ const [orderBookApproval, orderBookApprovalCallback] = useApproveCallback(orderFee, ORDER_BOOK_ADDRESS[chainId])
+ const approvalCallback = useCallback(async () => {
+ if (limitOrderApproval === ApprovalState.NOT_APPROVED) {
+ await limitOrderApprovalCallback()
+ }
+ if (orderBookApproval === ApprovalState.NOT_APPROVED) {
+ await orderBookApprovalCallback()
+ }
+ }, [limitOrderApproval, orderBookApproval, limitOrderApprovalCallback, orderBookApprovalCallback])
+
+ // check if user has gone through orderBookApproval process, used to show two step buttons, reset on token change
+ const [approvalSubmitted, setApprovalSubmitted] = useState(false)
+
+ // mark when a user has submitted an orderBookApproval, reset onTokenSelection for input field
+ useEffect(() => {
+ if (limitOrderApproval === ApprovalState.PENDING || orderBookApproval === ApprovalState.PENDING) {
+ setApprovalSubmitted(true)
+ }
+ }, [limitOrderApproval, orderBookApproval, approvalSubmitted])
+
+ const getColor = () => {
+ return buying ? (aboveMarketPrice ? theme.green1 : theme.red1) : aboveMarketPrice ? theme.red1 : theme.green1
+ }
+
+ // the callback to execute the swap
+ const { callback: swapCallback } = useTradeCallback(tradeToConfirm, allowedSlippage, recipient)
+
+ const handlePlaceOrder = useCallback(() => {
+ if (!swapCallback) {
+ return
+ }
+ setSwapState({ attemptingTxn: true, tradeToConfirm, showConfirm, swapErrorMessage: undefined, txHash: undefined })
+ swapCallback()
+ .then((hash) => {
+ setSwapState({ attemptingTxn: false, tradeToConfirm, showConfirm, swapErrorMessage: undefined, txHash: hash })
+
+ ReactGA.event({
+ category: 'Limit Order',
+ action:
+ recipient === null
+ ? 'Limit Order w/o Send'
+ : (recipientAddress ?? recipient) === account
+ ? 'Limit Order w/o Send + recipient'
+ : 'Limit Order w/ Send',
+ label: [trade?.inputAmount?.currency?.symbol, trade?.outputAmount?.currency?.symbol].join('/'),
+ })
+ })
+ .catch((error) => {
+ setSwapState({
+ attemptingTxn: false,
+ tradeToConfirm,
+ showConfirm,
+ swapErrorMessage: error.message,
+ txHash: undefined,
+ })
+ })
+ }, [swapCallback, tradeToConfirm, showConfirm, recipient, recipientAddress, account, trade])
+
+ // errors
+ const [showInverted, setShowInverted] = useState(false)
+
+ // 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
+ const showApproveFlow =
+ (!limitOrderInputError &&
+ (orderBookApproval === ApprovalState.NOT_APPROVED ||
+ orderBookApproval === ApprovalState.PENDING ||
+ (approvalSubmitted && orderBookApproval === ApprovalState.APPROVED))) ||
+ limitOrderApproval === ApprovalState.NOT_APPROVED ||
+ limitOrderApproval === ApprovalState.PENDING ||
+ (approvalSubmitted && limitOrderApproval === ApprovalState.APPROVED)
+
+ const handleConfirmDismiss = useCallback(() => {
+ setSwapState({ showConfirm: false, tradeToConfirm, attemptingTxn, swapErrorMessage, txHash })
+ // if there was a tx hash, we want to clear the input
+ if (txHash) {
+ onUserInput(Field.TOKEN, '')
+ onUserInput(Field.PRICE, '')
+ }
+ }, [attemptingTxn, onUserInput, swapErrorMessage, tradeToConfirm, txHash])
+
+ const handleAcceptChanges = useCallback(() => {
+ setSwapState({ tradeToConfirm: trade, swapErrorMessage, txHash, attemptingTxn, showConfirm })
+ }, [attemptingTxn, showConfirm, swapErrorMessage, trade, txHash])
+
+ const handlePriceSelect = useCallback(
+ (inputCurrency) => {
+ buying && setApprovalSubmitted(false) // reset 2 step UI for approvals
+ onCurrencySelection(Field.PRICE, inputCurrency)
+ },
+ [buying, onCurrencySelection]
+ )
+
+ const handleTokenSelect = useCallback(
+ (outputCurrency) => {
+ !buying && setApprovalSubmitted(false) // reset 2 step UI for approvals
+ onCurrencySelection(Field.TOKEN, outputCurrency)
+ },
+ [buying, onCurrencySelection]
+ )
+
+ const swapIsUnsupported = useIsTransactionUnsupported(currencies?.PRICE, currencies?.TOKEN)
+
+ const walletIsSupported =
+ walletType === WalletTypes.MetaMask ||
+ walletType === WalletTypes.CeloExtensionWallet ||
+ walletType === WalletTypes.PrivateKey ||
+ walletType === WalletTypes.Injected
+
+ return (
+ <>
+ {!walletIsSupported && (
+
+
+
+
+
+
+ Notice
+
+
+
+ You must be connected to a Metamask wallet to place limit orders
+
+ {' '}
+
+
+
+
+
+ )}
+
+
+
+ setBuying(true)}>
+ Buy
+
+ setBuying(false)}>
+ Sell
+
+
+
+
+
+
+
+ {buying ? 'Buy' : 'Sell'} Amount
+
+
+
+ Limit Price
+
+
+
+
+
+ <>
+
+ {marketPriceDiffIndicator && (
+
+
+ {marketPriceDiffIndicator.toSignificant(4)}% {aboveMarketPrice ? 'below' : 'above'}
+
+
+ market price
+
+
+ )}
+
+
+
+ Market Price
+
+ {trade ? (
+
+ ) : (
+ -
+ )}
+
+
+
+ Order Reward
+
+
+ {reward?.toSignificant(2) ?? '-'} {reward?.currency.symbol}
+
+
+
+
+ Order Fee
+
+
+ {orderFee?.toSignificant(2) ?? '-'} {orderFee?.currency.symbol}
+
+
+
+
+ Total
+
+
+ {parsedInputTotal && orderFee ? parsedInputTotal.add(orderFee).toSignificant(6) : '-'}{' '}
+ {parsedInputTotal?.currency.symbol}
+
+
+ >
+
+
+
+
+ {swapIsUnsupported ? (
+
+ Unsupported Asset
+
+ ) : !account ? (
+ {t('connectWallet')}
+ ) : showRamp ? (
+ {
+ new RampInstantSDK({
+ hostAppName: 'Ubeswap',
+ hostLogoUrl: 'https://info.ubeswap.org/favicon.png',
+ userAddress: account,
+ swapAsset: parsedInputTotal?.currency.symbol,
+ hostApiKey: process.env.REACT_APP_RAMP_KEY,
+ }).show()
+ }}
+ >
+ Get more {parsedInputTotal?.currency.symbol} via Ramp
+
+ ) : (
+
+
+ {limitOrderApproval === ApprovalState.PENDING || orderBookApproval === ApprovalState.PENDING ? (
+
+ Approving
+
+ ) : approvalSubmitted && orderBookApproval === ApprovalState.APPROVED ? (
+ 'Approved'
+ ) : (
+ 'Approve ' + (currencies[buying ? Field.PRICE : Field.TOKEN]?.symbol ?? '')
+ )}
+
+ {
+ if (parsedInputTotal && parsedOutputTotal) {
+ queueLimitOrderCallback({
+ inputAmount: parsedInputTotal,
+ outputAmount: parsedOutputTotal,
+ chainId: chainId,
+ })
+ }
+ }}
+ width="48%"
+ id="swap-button"
+ disabled={
+ !isValid ||
+ limitOrderApproval !== ApprovalState.APPROVED ||
+ orderBookApproval !== ApprovalState.APPROVED
+ }
+ altDisabledStyle={queueOrderLoading} // show solid button while waiting
+ paddingY="14px"
+ >
+
+
+ {t('placeOrder')}
+
+ {queueOrderLoading && }
+
+
+
+ )}
+ {showApproveFlow && (
+
+
+
+ )}
+
+
+
+
+ >
+ )
+}
diff --git a/src/pages/LimitOrder/redirects.tsx b/src/pages/LimitOrder/redirects.tsx
new file mode 100644
index 00000000..48680c38
--- /dev/null
+++ b/src/pages/LimitOrder/redirects.tsx
@@ -0,0 +1,42 @@
+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/LimitOrder/useCancelOrderCallback.tsx b/src/pages/LimitOrder/useCancelOrderCallback.tsx
new file mode 100644
index 00000000..6a475807
--- /dev/null
+++ b/src/pages/LimitOrder/useCancelOrderCallback.tsx
@@ -0,0 +1,38 @@
+import { useContractKit, useProvider } from '@celo-tools/use-contractkit'
+import { ChainId } from '@ubeswap/sdk'
+import { useDoTransaction } from 'components/swap/routing'
+import { useMemo } from 'react'
+
+import { executeCancelOrder } from './executCancelOrder'
+
+/**
+ * Use callback to cancel open limit order
+ * @param orderHash the hash of the order to cancel
+ * @returns
+ */
+export const useCancelOrderCallback = (
+ orderHash: string | undefined // orderHash of order to cancel
+): { callback: null | (() => Promise); error: string | null } => {
+ const { address: account, network } = useContractKit()
+ const library = useProvider()
+ const chainId = network.chainId as unknown as ChainId
+ const doTransaction = useDoTransaction()
+
+ return useMemo(() => {
+ if (!library || !orderHash || !account) {
+ return { callback: null, error: 'Missing dependencies' }
+ }
+
+ if (chainId === ChainId.BAKLAVA) {
+ return { callback: null, error: 'Baklava is not supported' }
+ }
+
+ const signer = library.getSigner(account)
+ const env = { signer, chainId, doTransaction }
+
+ return {
+ callback: async () => (await executeCancelOrder({ ...env, orderHash })).hash,
+ error: null,
+ }
+ }, [library, chainId, doTransaction, orderHash, account])
+}
diff --git a/src/pages/LimitOrder/useOrderBroadcasted.tsx b/src/pages/LimitOrder/useOrderBroadcasted.tsx
new file mode 100644
index 00000000..3ad5c32a
--- /dev/null
+++ b/src/pages/LimitOrder/useOrderBroadcasted.tsx
@@ -0,0 +1,125 @@
+import { useContractKit, useProvider } from '@celo-tools/use-contractkit'
+import { ChainId } from '@ubeswap/sdk'
+import { BigNumber } from 'ethers'
+import { OrderBook__factory } from 'generated'
+import { useLimitOrderProtocolContract } from 'hooks/useContract'
+import React, { useEffect } from 'react'
+import { useSingleContractMultipleData } from 'state/multicall/hooks'
+
+import { LIMIT_ORDER_ADDRESS, ORDER_BOOK_ADDRESS } from '../../constants'
+
+// TODO: Just do batch fetching in the future
+const CREATION_BLOCK = 10_000_000
+
+export interface LimitOrdersHistory {
+ orderHash: string
+ isOrderOpen: boolean
+ makingAmount: BigNumber
+ takingAmount: BigNumber
+ makerAsset: string
+ takerAsset: string
+ remaining: BigNumber
+ transactionHash: string
+}
+
+export interface LimitOrderRewards {
+ rewardRate: BigNumber
+ makerCurrencyAddress: string
+}
+
+type OrderBookEvent = {
+ maker: string
+ orderHash: string
+ order: {
+ salt: BigNumber
+ makerAsset: string
+ takerAsset: string
+ maker: string
+ receiver: string
+ allowedSender: string
+ makingAmount: BigNumber
+ takingAmount: BigNumber
+ makerAssetData: string
+ takerAssetData: string
+ getMakerAmount: string
+ getTakerAmount: string
+ predicate: string
+ permit: string
+ interaction: string
+ }
+ signature: string
+ transactionHash: string
+}
+
+export const useOrderBroadcasted = () => {
+ const { account, network } = useContractKit()
+ const provider = useProvider()
+ const chainId = network.chainId as unknown as ChainId
+ const orderBookAddr = ORDER_BOOK_ADDRESS[chainId]
+
+ const [orderBroadcasts, setOrderBroadcasts] = React.useState([])
+ const call = React.useCallback(async () => {
+ if (!account) {
+ return
+ }
+ const orderBook = OrderBook__factory.connect(orderBookAddr, provider)
+ const orderBookEvents = await orderBook.queryFilter(
+ orderBook.filters['OrderBroadcasted'](account),
+ CREATION_BLOCK,
+ 'latest'
+ )
+ const orderBroadcasts = orderBookEvents.map((orderBookEvent) => {
+ return {
+ ...orderBookEvent.args,
+ transactionHash: orderBookEvent.transactionHash,
+ }
+ })
+ setOrderBroadcasts(orderBroadcasts)
+ }, [account, orderBookAddr, provider])
+
+ useEffect(() => {
+ const timer = setInterval(call, 5000)
+ return () => {
+ clearInterval(timer)
+ }
+ }, [call])
+
+ return orderBroadcasts
+}
+
+export const useLimitOrdersHistory = (): LimitOrdersHistory[] => {
+ const orderEvents = useOrderBroadcasted()
+ const { network } = useContractKit()
+ const chainId = network.chainId as unknown as ChainId
+ const limitOrderAddr = LIMIT_ORDER_ADDRESS[chainId]
+
+ const limitOrderProtocol = useLimitOrderProtocolContract(limitOrderAddr)
+ const remainingsRaw = useSingleContractMultipleData(
+ limitOrderProtocol,
+ 'remainingRaw',
+ orderEvents.map(({ orderHash }) => [orderHash])
+ )
+ const remainings = remainingsRaw?.find((result) => !result.result)
+ ? null
+ : (remainingsRaw.map((r) => r.result?.[0] ?? BigNumber.from(0)) as readonly BigNumber[])
+
+ return React.useMemo(() => {
+ if (!remainings) return []
+
+ return orderEvents.map(({ orderHash, order, transactionHash }, idx) => {
+ const { makerAsset, takerAsset, makingAmount, takingAmount } = order
+ const remaining = remainings[idx].eq(0) ? makingAmount : remainings[idx].sub(1)
+
+ return {
+ orderHash,
+ makerAsset,
+ takerAsset,
+ isOrderOpen: remaining.gt(0),
+ remaining,
+ makingAmount,
+ takingAmount,
+ transactionHash,
+ }
+ })
+ }, [orderEvents, remainings])
+}
diff --git a/src/pages/Stake/index.tsx b/src/pages/Stake/index.tsx
index ad735340..c5de6c57 100644
--- a/src/pages/Stake/index.tsx
+++ b/src/pages/Stake/index.tsx
@@ -1,7 +1,7 @@
import { useContractKit, useGetConnectedSigner } from '@celo-tools/use-contractkit'
import { TokenAmount } from '@ubeswap/sdk'
import { ButtonEmpty, ButtonLight, ButtonPrimary, ButtonRadio } from 'components/Button'
-import { GreyCard, YellowCard } from 'components/Card'
+import { GreyCard, LightCard } from 'components/Card'
import { AutoColumn } from 'components/Column'
import CurrencyLogo from 'components/CurrencyLogo'
import { CardNoise, CardSection, DataCard } from 'components/earn/styled'
@@ -215,9 +215,9 @@ export const Stake: React.FC = () => {
{/* Your UBE stake: {stakeBalance ? stakeBalance.toFixed(2, { groupSeparator: ',' }) : '--'} UBE
*/}
{userRewardRate?.greaterThan('0') ? (
<>
-
+
- Your weekly rewards
+ Your Weekly Rewards
{userRewardRate
? userRewardRate.multiply(BIG_INT_SECONDS_IN_WEEK).toFixed(2, { groupSeparator: ',' })
@@ -226,7 +226,7 @@ export const Stake: React.FC = () => {
- Annual stake APR
+ Annual Stake APR
{apy?.multiply('100').toFixed(2, { groupSeparator: ',' }) ?? '--'}%{' '}
@@ -234,15 +234,15 @@ export const Stake: React.FC = () => {
Unclaimed Rewards
-
- {userRewardRate ? earned.toFixed(4, { groupSeparator: ',' }) : '--'}
-
{t('claim')}
+
+ {userRewardRate ? earned.toFixed(4, { groupSeparator: ',' }) : '--'}
+
-
+
Community UBE Stake
@@ -254,14 +254,14 @@ export const Stake: React.FC = () => {
{stakeBalance?.toSignificant(4)}
View UBE Contract
@@ -304,10 +304,10 @@ export const Stake: React.FC = () => {
- Your UBE stake
-
+
Your UBE Stake
+
setStaking(true)}>
Stake
diff --git a/src/state/application/actions.ts b/src/state/application/actions.ts
index cd8f456c..acbdea27 100644
--- a/src/state/application/actions.ts
+++ b/src/state/application/actions.ts
@@ -28,6 +28,7 @@ export enum ApplicationModal {
MENU,
DELEGATE,
VOTE,
+ CHARTS,
}
export const updateBlockNumber = createAction<{ chainId: number; blockNumber: number }>('application/updateBlockNumber')
diff --git a/src/state/index.ts b/src/state/index.ts
index cbd4c394..755e23f9 100644
--- a/src/state/index.ts
+++ b/src/state/index.ts
@@ -4,6 +4,7 @@ import { load, save } from 'redux-localstorage-simple'
import application from './application/reducer'
import burn from './burn/reducer'
import { updateVersion } from './global/actions'
+import limit from './limit/reducer'
import lists from './lists/reducer'
import mint from './mint/reducer'
import multicall from './multicall/reducer'
@@ -19,6 +20,7 @@ const store = configureStore({
user,
transactions,
swap,
+ limit,
mint,
burn,
multicall,
diff --git a/src/state/limit/actions.ts b/src/state/limit/actions.ts
new file mode 100644
index 00000000..ef46103d
--- /dev/null
+++ b/src/state/limit/actions.ts
@@ -0,0 +1,21 @@
+import { createAction } from '@reduxjs/toolkit'
+
+export enum Field {
+ TOKEN = 'TOKEN',
+ PRICE = 'PRICE',
+}
+
+export const selectCurrency = createAction<{ field: Field; currencyId: string }>('limit/selectCurrency')
+export const switchCurrencies = createAction('limit/switchCurrencies')
+export const typeInput = createAction<{ field: Field; typedValue: string }>('limit/typeInput')
+export const setBuying = createAction<{ buying: boolean }>('limit/setBuying')
+export const replaceLimitState = createAction<{
+ field: Field
+ priceTypedValue: string
+ tokenTypedValue: string
+ priceCurrencyId?: string
+ tokenCurrencyId?: string
+ recipient: string | null
+ buying: boolean
+}>('limit/replaceLimitState')
+export const setRecipient = createAction<{ recipient: string | null }>('limit/setRecipient')
diff --git a/src/state/limit/hooks.ts b/src/state/limit/hooks.ts
new file mode 100644
index 00000000..3f39ad76
--- /dev/null
+++ b/src/state/limit/hooks.ts
@@ -0,0 +1,245 @@
+import { useContractKit } from '@celo-tools/use-contractkit'
+import { parseUnits } from '@ethersproject/units'
+import { CELO, cEUR, ChainId as UbeswapChainId, cUSD, Fraction, Token, TokenAmount } from '@ubeswap/sdk'
+import { useUbeswapTradeExactIn, useUbeswapTradeExactOut } from 'components/swap/routing/hooks/useTrade'
+import { UbeswapTrade } from 'components/swap/routing/trade'
+import { useCallback } from 'react'
+import { useDispatch, useSelector } from 'react-redux'
+import { tryParseAmount } from 'state/swap/hooks'
+import { useCurrencyBalances } from 'state/wallet/hooks'
+
+import { ROUTER_ADDRESS } from '../../constants'
+import { useCurrency } from '../../hooks/Tokens'
+import useENS from '../../hooks/useENS'
+import { isAddress } from '../../utils'
+import { AppDispatch, AppState } from '../index'
+import {
+ Field,
+ selectCurrency,
+ setBuying as setBuyingAction,
+ setRecipient,
+ switchCurrencies,
+ typeInput,
+} from './actions'
+
+const BAD_RECIPIENT_ADDRESSES: string[] = [
+ '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f', // v2 factory
+ '0xf164fC0Ec4E93095b804a4795bBe1e041497b92a', // v2 router 01
+ '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D', // v2 router 02
+ ROUTER_ADDRESS,
+]
+
+export function useLimitOrderState(): AppState['limit'] {
+ return useSelector((state) => state.limit)
+}
+
+// from the current limit order inputs, compute the trade
+export function useDerivedLimitOrderInfo(): {
+ currencies: { [field in Field]?: Token }
+ currencyBalances: { [field in Field]?: TokenAmount }
+ parsedInputTotal: TokenAmount | undefined
+ parsedOutputTotal: TokenAmount | undefined
+ v2Trade: UbeswapTrade | undefined
+ inputError?: string
+ showRamp: boolean
+ buying: boolean
+} {
+ const { address: account, network } = useContractKit()
+
+ const {
+ priceTypedValue,
+ tokenTypedValue,
+ [Field.PRICE]: { currencyId: priceCurrencyId },
+ [Field.TOKEN]: { currencyId: tokenCurrencyId },
+ recipient,
+ buying,
+ } = useLimitOrderState()
+
+ const priceCurrency = useCurrency(priceCurrencyId)
+ const tokenCurrency = useCurrency(tokenCurrencyId)
+ const recipientLookup = useENS(recipient ?? undefined)
+ const to: string | null = (recipient === null ? account : recipientLookup.address) ?? null
+
+ const relevantTokenBalances = useCurrencyBalances(account ?? undefined, [
+ priceCurrency ?? undefined,
+ tokenCurrency ?? undefined,
+ ])
+
+ // When buying, the buy asset
+ const parsedTokenAmount = tryParseAmount(tokenTypedValue, tokenCurrency ?? undefined)
+ const parsedPrice = tryParseAmount(priceTypedValue, priceCurrency ?? undefined)
+ // When buying, the sell asset
+ const parsedTokenOutput = parsedPrice
+ ? tryParseAmount(
+ parsedTokenAmount?.multiply(parsedPrice.numerator).divide(parsedPrice.denominator).toFixed(10),
+ priceCurrency ?? undefined
+ )
+ : undefined
+
+ const parsedInputTotal = buying ? parsedTokenOutput : parsedTokenAmount
+ const parsedOutputTotal = buying ? parsedTokenAmount : parsedTokenOutput
+
+ // Determine price as if we were trading 1 of the asset
+ const buyTrade = useUbeswapTradeExactOut(
+ priceCurrency ?? undefined,
+ parsedTokenAmount
+ ? new TokenAmount(parsedTokenAmount.currency, parseUnits('1', parsedTokenAmount.currency.decimals).toString())
+ : undefined
+ )
+ const sellTrade = useUbeswapTradeExactIn(
+ parsedTokenAmount
+ ? new TokenAmount(parsedTokenAmount.currency, parseUnits('1', parsedTokenAmount.currency.decimals).toString())
+ : undefined,
+ priceCurrency ?? undefined
+ )
+ const v2Trade = buying ? buyTrade : sellTrade
+
+ const currencyBalances = {
+ [Field.PRICE]: relevantTokenBalances[0],
+ [Field.TOKEN]: relevantTokenBalances[1],
+ }
+
+ const currencies: { [field in Field]?: Token } = {
+ [Field.PRICE]: priceCurrency ?? undefined,
+ [Field.TOKEN]: tokenCurrency ?? undefined,
+ }
+
+ let inputError: string | undefined
+ if (!account) {
+ inputError = 'Connect Wallet'
+ }
+
+ if (!parsedTokenAmount) {
+ inputError = inputError ?? 'Enter a token amount'
+ } else if (!parsedPrice) {
+ inputError = inputError ?? 'Enter a limit order price'
+ }
+
+ if (!currencies[Field.TOKEN] || !currencies[Field.PRICE]) {
+ inputError = inputError ?? 'Select a token'
+ }
+
+ const formattedTo = isAddress(to)
+ if (!to || !formattedTo) {
+ inputError = inputError ?? 'Enter a recipient'
+ } else {
+ if (BAD_RECIPIENT_ADDRESSES.indexOf(formattedTo) !== -1) {
+ inputError = inputError ?? 'Invalid recipient'
+ }
+ }
+
+ // compare input balance to max input based on version
+ const [balanceIn, amountIn] = [
+ buying ? currencyBalances[Field.PRICE] : currencyBalances[Field.TOKEN],
+ parsedInputTotal,
+ ]
+
+ let showRamp = false
+ if (balanceIn && amountIn && balanceIn.lessThan(amountIn)) {
+ if (
+ balanceIn.currency.address === cUSD[network.chainId as unknown as UbeswapChainId].address ||
+ balanceIn.currency.address === CELO[network.chainId as unknown as UbeswapChainId].address ||
+ balanceIn.currency.address === cEUR[network.chainId as unknown as UbeswapChainId].address
+ ) {
+ showRamp = true
+ }
+ inputError = 'Insufficient ' + amountIn.currency.symbol + ' balance'
+ }
+
+ return {
+ currencies,
+ currencyBalances,
+ parsedInputTotal,
+ parsedOutputTotal,
+ v2Trade: v2Trade ?? undefined,
+ showRamp,
+ inputError,
+ buying,
+ }
+}
+
+export function useMarketPriceDiff(): {
+ marketPriceDiffIndicator: Fraction | undefined
+ aboveMarketPrice: boolean | undefined
+} {
+ const {
+ priceTypedValue,
+ [Field.PRICE]: { currencyId: priceCurrencyId },
+ } = useLimitOrderState()
+ const { v2Trade: trade, buying } = useDerivedLimitOrderInfo()
+
+ const priceCurrency = useCurrency(priceCurrencyId)
+ const parsedPrice = tryParseAmount(priceTypedValue, priceCurrency ?? undefined)
+ const marketDiffFraction =
+ trade && parsedPrice
+ ? new Fraction(
+ parsedPrice?.numerator,
+ buying ? trade.executionPrice.invert().numerator : trade.executionPrice.invert().denominator
+ )
+ : undefined
+ const aboveMarketPrice = marketDiffFraction && marketDiffFraction.lessThan('1')
+ const marketPriceDiffIndicator = marketDiffFraction
+ ? aboveMarketPrice
+ ? new Fraction('1', '1').subtract(marketDiffFraction).multiply(new Fraction('1000', '10'))
+ : marketDiffFraction.subtract(new Fraction('1', '1')).multiply(new Fraction('1000', '10'))
+ : undefined
+
+ return {
+ marketPriceDiffIndicator,
+ aboveMarketPrice,
+ }
+}
+
+export function useLimitOrderActionHandlers(): {
+ onCurrencySelection: (field: Field, currency: Token) => void
+ onSwitchTokens: () => void
+ onUserInput: (field: Field, typedValue: string) => void
+ onChangeRecipient: (recipient: string | null) => void
+ setBuying: (buying: boolean) => void
+} {
+ const dispatch = useDispatch()
+ const onCurrencySelection = useCallback(
+ (field: Field, currency: Token) => {
+ dispatch(
+ selectCurrency({
+ field,
+ currencyId: currency instanceof Token ? currency.address : '',
+ })
+ )
+ },
+ [dispatch]
+ )
+
+ const onSwitchTokens = useCallback(() => {
+ dispatch(switchCurrencies())
+ }, [dispatch])
+
+ const onUserInput = useCallback(
+ (field: Field, typedValue: string) => {
+ dispatch(typeInput({ field, typedValue }))
+ },
+ [dispatch]
+ )
+
+ const onChangeRecipient = useCallback(
+ (recipient: string | null) => {
+ dispatch(setRecipient({ recipient }))
+ },
+ [dispatch]
+ )
+
+ const setBuying = useCallback(
+ (buying: boolean) => {
+ dispatch(setBuyingAction({ buying }))
+ },
+ [dispatch]
+ )
+
+ return {
+ onSwitchTokens,
+ onCurrencySelection,
+ onUserInput,
+ onChangeRecipient,
+ setBuying,
+ }
+}
diff --git a/src/state/limit/reducer.ts b/src/state/limit/reducer.ts
new file mode 100644
index 00000000..b46c60fa
--- /dev/null
+++ b/src/state/limit/reducer.ts
@@ -0,0 +1,107 @@
+import { createReducer } from '@reduxjs/toolkit'
+
+import {
+ Field,
+ replaceLimitState,
+ selectCurrency,
+ setBuying,
+ setRecipient,
+ switchCurrencies,
+ typeInput,
+} from './actions'
+
+export interface LimitState {
+ readonly priceTypedValue: string
+ readonly tokenTypedValue: string
+ readonly [Field.TOKEN]: {
+ readonly currencyId: string | undefined
+ }
+ readonly [Field.PRICE]: {
+ readonly currencyId: string | undefined
+ }
+ // the typed recipient address or ENS name, or null if Limit should go to sender
+ readonly recipient: string | null
+ readonly buying: boolean
+}
+
+const initialState: LimitState = {
+ priceTypedValue: '',
+ tokenTypedValue: '',
+ [Field.TOKEN]: {
+ currencyId: '',
+ },
+ [Field.PRICE]: {
+ currencyId: '',
+ },
+ recipient: null,
+ buying: true,
+}
+
+export default createReducer(initialState, (builder) =>
+ builder
+ .addCase(
+ replaceLimitState,
+ (
+ state,
+ { payload: { tokenTypedValue, priceTypedValue, recipient, field, tokenCurrencyId, priceCurrencyId, buying } }
+ ) => {
+ return {
+ [Field.TOKEN]: {
+ currencyId: tokenCurrencyId,
+ },
+ [Field.PRICE]: {
+ currencyId: priceCurrencyId,
+ },
+ independentField: field,
+ tokenTypedValue,
+ priceTypedValue,
+ recipient,
+ buying,
+ }
+ }
+ )
+ .addCase(selectCurrency, (state, { payload: { currencyId, field } }) => {
+ const otherField = field === Field.TOKEN ? Field.PRICE : Field.TOKEN
+ if (currencyId === state[otherField].currencyId) {
+ // the case where we have to Limit the order
+ return {
+ ...state,
+ [field]: { currencyId: currencyId },
+ [otherField]: { currencyId: state[field].currencyId },
+ }
+ } else {
+ // the normal case
+ return {
+ ...state,
+ [field]: { currencyId: currencyId },
+ }
+ }
+ })
+ .addCase(switchCurrencies, (state) => {
+ return {
+ ...state,
+ [Field.TOKEN]: { currencyId: state[Field.PRICE].currencyId },
+ [Field.PRICE]: { currencyId: state[Field.TOKEN].currencyId },
+ }
+ })
+ .addCase(typeInput, (state, { payload: { field, typedValue } }) => {
+ if (field === Field.PRICE) {
+ return {
+ ...state,
+ priceTypedValue: typedValue,
+ }
+ } else if (field === Field.TOKEN) {
+ return {
+ ...state,
+ tokenTypedValue: typedValue,
+ }
+ }
+ return state
+ })
+ .addCase(setRecipient, (state, { payload: { recipient } }) => {
+ state.recipient = recipient
+ })
+ .addCase(setBuying, (state, { payload: { buying } }) => {
+ state.buying = buying
+ })
+)
diff --git a/src/state/stake/farms.ts b/src/state/stake/farms.ts
deleted file mode 100644
index 89c38f48..00000000
--- a/src/state/stake/farms.ts
+++ /dev/null
@@ -1,295 +0,0 @@
-export type MultiRewardPool = {
- address: string
- underlyingPool: string
- basePool: string
- numRewards: number
- active: boolean
-}
-
-export const multiRewardPools: MultiRewardPool[] = [
- // ** Friends ** //
- // CELO-MOBI
- {
- address: '0xb450940c5297e9b5e7167FAC5903fD1e90b439b8',
- underlyingPool: '0xd930501A0848DC0AA3E301c7B9b8AFE8134D7f5F',
- basePool: '0x19F1A692C77B481C23e9916E3E83Af919eD49765',
- numRewards: 3,
- active: true,
- },
- // mCUSD-mcEUR
- {
- address: '0x2Ca16986bEA18D562D26354b4Ff4C504F14fB01c',
- underlyingPool: '0xF554690b1A996893c4DEBadc57b759350dc10b29',
- basePool: '0xaAA7bf214367572cadbF17f17d8E035742b55ab9',
- numRewards: 3,
- active: true,
- },
- // MOO-mCELO
- {
- address: '0xE76525610652fFC3aF751Ab0dcC3448B345051F6',
- underlyingPool: '0xBA7dCc70c68e11633d7DACBAFa493Af61D0c5B1d',
- basePool: '0x54097E406DFC00B9179167F9E20B26406Ad42f0F',
- numRewards: 3,
- active: true,
- },
- // POOF-UBE
- {
- address: '0x4274AA72B12221D32ca77cB37057A9692E0b59Eb',
- underlyingPool: '0xC88B8d622c0322fb59ae4473D7A1798DE60785dD',
- basePool: '0xC88B8d622c0322fb59ae4473D7A1798DE60785dD',
- numRewards: 2,
- active: true,
- },
- // pCELO-POOF
- {
- address: '0x999DF57c86EcC98E0e55f5f1dEdD102A89F25e8e',
- underlyingPool: '0x66B6e41AC12d51918775410D3331B21F7851a8Bc',
- basePool: '0x66B6e41AC12d51918775410D3331B21F7851a8Bc',
- numRewards: 2,
- active: true,
- },
- // pCELO-POOF
- {
- address: '0x7B7F08164036abEbafD1bf75c1464c6F0d01653C',
- underlyingPool: '0xd60E0034D4B27DE226EFf13f68249F69d4D6Cb38',
- basePool: '0xd60E0034D4B27DE226EFf13f68249F69d4D6Cb38',
- numRewards: 2,
- active: false,
- },
- // KNX-CELO
- {
- address: '0x1f1678Cc7358F4ed808B53733Bc49c4CFFe8A075',
- underlyingPool: '0x7313fDf9D8Cab87E54efc8905B9D7d4BA3Fe7c8D',
- basePool: '0x7313fDf9D8Cab87E54efc8905B9D7d4BA3Fe7c8D',
- numRewards: 2,
- active: true,
- },
- // TFBX-UBE
- {
- address: '0x501ba7c59BA8afC1427F75D310A862BA0D2adcD2',
- underlyingPool: '0x3DAc201Ec1b3a037bC9124906A2ae0A6a09ACC1d',
- basePool: '0x3DAc201Ec1b3a037bC9124906A2ae0A6a09ACC1d',
- numRewards: 2,
- active: true,
- },
- // SOURCE-mcUSD
- {
- address: '0xF4662e4E254006939c2198cb6F61635b03fd14Eb',
- underlyingPool: '0x5F5c3eEa2b9e65f667E34C70Db68f62bbbFC9188',
- basePool: '0x9cAF0Cd20C8eF7622EEb8dB50e5bB4d407e38AE2',
- numRewards: 3,
- active: true,
- },
- // ARI-CELO
- {
- address: '0xEfe2f9d62E45815837b4f20c1F44F0A83605B540',
- underlyingPool: '0xFEB0df4542E5394aAc89383c135E2Fc829812C6c',
- basePool: '0xFEB0df4542E5394aAc89383c135E2Fc829812C6c',
- numRewards: 2,
- active: true,
- },
- // PACT-CELO
- {
- address: '0x6F11B6eA70DEe4f167b1A4ED1F01C903f6781960',
- underlyingPool: '0xFc26229c90E6236fC85c492b738c6E496c177cd0',
- basePool: '0x833Febc01260D8f3DCc98393c216A025E90b405d',
- numRewards: 3,
- active: true,
- },
- // ABR-mcUSD
- {
- address: '0x3904056570Ca95Cc3371E349A7733b5BfaeD64e5',
- underlyingPool: '0xD94E14358f66A3C0D13ae76ec45Fe1c92Dd7Fb23',
- basePool: '0xD94E14358f66A3C0D13ae76ec45Fe1c92Dd7Fb23',
- numRewards: 2,
- active: true,
- },
-
- // ** D4P ** //
- // UBE-CELO
- {
- address: '0x9D87c01672A7D02b2Dc0D0eB7A145C7e13793c3B',
- underlyingPool: '0x295D6f96081fEB1569d9Ce005F7f2710042ec6a1',
- basePool: '0x295D6f96081fEB1569d9Ce005F7f2710042ec6a1',
- numRewards: 2,
- active: true,
- },
- // rCELO-CELO
- {
- address: '0x194478Aa91e4D7762c3E51EeE57376ea9ac72761',
- underlyingPool: '0xD7D6b5213b9B9DFffbb7ef008b3cF3c677eb2468',
- basePool: '0xD7D6b5213b9B9DFffbb7ef008b3cF3c677eb2468',
- numRewards: 2,
- active: true,
- },
- // CELO-mcUSD
- {
- address: '0x161c77b4919271B7ED59AdB2151FdaDe3F907a1F',
- underlyingPool: '0xcca933D2ffEDCa69495435049a878C4DC34B079d',
- basePool: '0xcca933D2ffEDCa69495435049a878C4DC34B079d',
- numRewards: 2,
- active: true,
- },
- // CELO-mcEUR
- {
- address: '0x728C650D1Fb4da2D18ccF4DF45Af70c5AEb09f81',
- underlyingPool: '0x32779E096bF913093933Ea94d31956AF8a763CE9',
- basePool: '0x32779E096bF913093933Ea94d31956AF8a763CE9',
- numRewards: 2,
- active: true,
- },
- // WBTC-mcUSD
- {
- address: '0xf3D9E027B131Af5162451601038EddBF456d824B',
- underlyingPool: '0x0079418D54F887e7859c7A3Ecc16cE96A416527b',
- basePool: '0x0079418D54F887e7859c7A3Ecc16cE96A416527b',
- numRewards: 2,
- active: false,
- },
- // WETH-mcUSD
- {
- address: '0xD6E28720Fcd1C1aB6da2d1043a6763FDBb67b3aA',
- underlyingPool: '0x666C59E75271f1fF5a52b58D4563afdc76a53b4e',
- basePool: '0x666C59E75271f1fF5a52b58D4563afdc76a53b4e',
- numRewards: 2,
- active: false,
- },
- // WETH-mcUSD
- {
- address: '0x81DDaFE15c01aDfda3dd8Fe9Bb984E64Cba606eB',
- underlyingPool: '0x1e41a9fd5a94def942ed46aa8bdb4a7f248efad3',
- basePool: '0x1e41a9fd5a94def942ed46aa8bdb4a7f248efad3',
- numRewards: 2,
- active: true,
- },
- // WBTC-mcUSD
- {
- address: '0xE6AD921bDa9F4971aBc8FA78cBD07AeB5c1A61ea',
- underlyingPool: '0xc6910dB4156B535966E4a7e8CcA7D39579b99A81',
- basePool: '0xc6910dB4156B535966E4a7e8CcA7D39579b99A81',
- numRewards: 2,
- active: true,
- },
- // SUSHI-mcUSD
- {
- address: '0x0E83662A17B8A3a0585DcA34E5BE81ea6bd59556',
- underlyingPool: '0xA2674f69B2BEf4ca3E75589aD4f4d36F061048a9',
- basePool: '0xA2674f69B2BEf4ca3E75589aD4f4d36F061048a9',
- numRewards: 2,
- active: true,
- },
- // CRV-mcUSD
- {
- address: '0x85B21208C0058019bc8004D85eFEa881E7598D17',
- underlyingPool: '0xA92Bb4D6399Be5403d6c8DF3cce4dd991ca8EaFc',
- basePool: '0xA92Bb4D6399Be5403d6c8DF3cce4dd991ca8EaFc',
- numRewards: 2,
- active: true,
- },
- // AAVE-mcUSD
- {
- address: '0x09c1cF8669f9A026c59EDd4792944a9aCd2d2a2E',
- underlyingPool: '0xF20448aaF8CC60432FC2E774F9ED965D4bf77cDc',
- basePool: '0xF20448aaF8CC60432FC2E774F9ED965D4bf77cDc',
- numRewards: 2,
- active: true,
- },
- // FTM-mcUSD
- {
- address: '0x3C29593674c5c760172d354acE88Da4D9d3EB64f',
- underlyingPool: '0x5704F21cF5C7e6556cBD1ceEbbD23752B68e4845',
- basePool: '0x5704F21cF5C7e6556cBD1ceEbbD23752B68e4845',
- numRewards: 2,
- active: true,
- },
- // AVAX-mcUSD
- {
- address: '0x750bB68Fa18F06d9696af85Ecc312f178E75fCfD',
- underlyingPool: '0x9584870281DD0d764748a2a234e2218AE544C614',
- basePool: '0x9584870281DD0d764748a2a234e2218AE544C614',
- numRewards: 2,
- active: true,
- },
- // BNB-mcUSD
- {
- address: '0xCD2d4024A42109593301fF11967c16eA180DD381',
- underlyingPool: '0x522be12487d0640337abCfC7201066eC8F787AC5',
- basePool: '0x522be12487d0640337abCfC7201066eC8F787AC5',
- numRewards: 2,
- active: true,
- },
- // WMATIC-mcUSD
- {
- address: '0x00C4aCee9eB84B1a6Cdc741AeEd19BF84CbE7bF5',
- underlyingPool: '0x80ED8Da2d3cd269B0ccbc6ddF8DA2807BF583307',
- basePool: '0x80ED8Da2d3cd269B0ccbc6ddF8DA2807BF583307',
- numRewards: 2,
- active: true,
- },
- // SOL-CELO
- {
- address: '0x83470506ba97dB33Df0EBe01E876C6718C762Df6',
- underlyingPool: '0x33cD870547DD6F30db86e7EE7707DC78e7825289',
- basePool: '0x33cD870547DD6F30db86e7EE7707DC78e7825289',
- numRewards: 2,
- active: true,
- },
-
- // ** Inactive ** //
- // CELO-MOBI
- {
- address: '0xd930501A0848DC0AA3E301c7B9b8AFE8134D7f5F',
- underlyingPool: '0x19F1A692C77B481C23e9916E3E83Af919eD49765',
- basePool: '0x19F1A692C77B481C23e9916E3E83Af919eD49765',
- numRewards: 2,
- active: false,
- },
- // CELO-mcUSDxOLD
- {
- address: '0xbbC8C824c638fd238178a71F5b1E5Ce7e4Ce586B',
- underlyingPool: '0x66bD2eF224318cA5e3A93E165e77fAb6DD986E89',
- basePool: '0x66bD2eF224318cA5e3A93E165e77fAb6DD986E89',
- numRewards: 2,
- active: false,
- },
- // CELO-mcEURxOLD
- {
- address: '0x0F3d01aea89dA0b6AD81712Edb96FA7AF1c17E9B',
- underlyingPool: '0x08252f2E68826950d31D268DfAE5E691EE8a2426',
- basePool: '0x08252f2E68826950d31D268DfAE5E691EE8a2426',
- numRewards: 2,
- active: false,
- },
- // mCUSDxOLD-mcEURxOLD
- {
- address: '0x2f0ddEAa9DD2A0FB78d41e58AD35373d6A81EbB0',
- underlyingPool: '0xaf13437122cd537C5D8942f17787cbDBd787fE94',
- basePool: '0xaf13437122cd537C5D8942f17787cbDBd787fE94',
- numRewards: 2,
- active: false,
- },
- // MOO-mCELOxOLD
- {
- address: '0x84Bb1795b699Bf7a798C0d63e9Aad4c96B0830f4',
- underlyingPool: '0xC087aEcAC0a4991f9b0e931Ce2aC77a826DDdaf3',
- basePool: '0xC087aEcAC0a4991f9b0e931Ce2aC77a826DDdaf3',
- numRewards: 2,
- active: false,
- },
- // mCUSDxOLD-mcEURxOLD
- {
- address: '0x3d823f7979bB3af846D8F1a7d98922514eA203fC',
- underlyingPool: '0xb030882bfc44e223fd5e20d8645c961be9b30bb3',
- basePool: '0xaf13437122cd537C5D8942f17787cbDBd787fE94',
- numRewards: 3,
- active: false,
- },
- // MOO-mCELOxOLD
- {
- address: '0x3c7beeA32A49D96d72ce45C7DeFb5b287479C2ba',
- underlyingPool: '0x8f309df7527f16dff49065d3338ea3f3c12b5d09',
- basePool: '0xC087aEcAC0a4991f9b0e931Ce2aC77a826DDdaf3',
- numRewards: 3,
- active: false,
- },
-]
diff --git a/src/state/stake/hooks.ts b/src/state/stake/hooks.ts
index c96af048..d3222ce5 100644
--- a/src/state/stake/hooks.ts
+++ b/src/state/stake/hooks.ts
@@ -1,20 +1,21 @@
-import { ChainId, useContractKit } from '@celo-tools/use-contractkit'
+import { ChainId, useContractKit, useProvider } from '@celo-tools/use-contractkit'
import { BigNumber } from '@ethersproject/bignumber'
import { ChainId as UbeswapChainId, JSBI, Pair, Token, TokenAmount } from '@ubeswap/sdk'
import { POOL_MANAGER } from 'constants/poolManager'
import { UBE } from 'constants/tokens'
-import { PoolManager } from 'generated/'
+import { MoolaStakingRewards__factory, PoolManager } from 'generated/'
import { useAllTokens } from 'hooks/Tokens'
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
import zip from 'lodash/zip'
// Hooks
-import { useMemo } from 'react'
+import React, { useEffect, useMemo } from 'react'
import ERC_20_INTERFACE from '../../constants/abis/erc20'
import { STAKING_REWARDS_INTERFACE } from '../../constants/abis/staking-rewards'
// Interfaces
import { UNISWAP_V2_PAIR_INTERFACE } from '../../constants/abis/uniswap-v2-pair'
import { usePoolManagerContract, useTokenContract } from '../../hooks/useContract'
+import { useFarmRegistry } from '../../pages/Earn/useFarmRegistry'
import {
NEVER_RELOAD,
useMultipleContractSingleData,
@@ -22,11 +23,12 @@ import {
useSingleContractMultipleData,
} from '../multicall/hooks'
import { tryParseAmount } from '../swap/hooks'
-import { multiRewardPools } from './farms'
import { useMultiStakeRewards } from './useDualStakeRewards'
import useStakingInfo from './useStakingInfo'
export const STAKING_GENESIS = 1619100000
+const ACTIVE_CONTRACT_UPDATED_THRESHOLD = 5259492
+const UNPREDICTABLE_GAS_LIMIT_ERROR_CODE = 'UNPREDICTABLE_GAS_LIMIT'
export interface StakingInfo {
// the address of the reward contract
@@ -61,13 +63,95 @@ export interface StakingInfo {
readonly rewardTokens: Token[]
}
+type MultiRewardPool = {
+ address: string
+ underlyingPool: string
+ basePool: string
+ numRewards: number
+ active: boolean
+}
+
+export const useMultiRewardPools = (): MultiRewardPool[] => {
+ const library = useProvider()
+ const farmSummaries = useFarmRegistry()
+
+ const [multiRewardPools, setMultiRewardPools] = React.useState([])
+
+ const call = React.useCallback(async () => {
+ const multiRwdPools: MultiRewardPool[] = []
+
+ await Promise.all(
+ farmSummaries.map(async (fs) => {
+ let poolContract = MoolaStakingRewards__factory.connect(fs.stakingAddress, library)
+ const rewardsTokens = []
+ const externalStakingRwdAddresses = []
+
+ // the first reward token at the top level
+ rewardsTokens.push(await poolContract.rewardsToken())
+
+ // last time the contract was updated - set isActive to false if it has been longer than 2 months
+ let periodFinish = await poolContract.periodFinish()
+ let isActive = Math.floor(Date.now() / 1000) - periodFinish.toNumber() < ACTIVE_CONTRACT_UPDATED_THRESHOLD
+
+ let baseContractFound = false
+ // recursivley find underlying and base pool contracts
+ while (!baseContractFound) {
+ try {
+ // find the underlying contract if one exists
+ const externalStakingRewardAddr = await poolContract.externalStakingRewards()
+ externalStakingRwdAddresses.push(externalStakingRewardAddr)
+
+ // capture the contract's reward token
+ poolContract = MoolaStakingRewards__factory.connect(externalStakingRewardAddr, library)
+ rewardsTokens.push(await poolContract.rewardsToken())
+
+ // determine if the underlying contract is active or not
+ periodFinish = await poolContract.periodFinish()
+ isActive =
+ Math.floor(Date.now() / 1000) - periodFinish.toNumber() < ACTIVE_CONTRACT_UPDATED_THRESHOLD || isActive
+ } catch (e: any) {
+ //if the error is not what is expected - log it
+ if (e.code !== UNPREDICTABLE_GAS_LIMIT_ERROR_CODE) {
+ console.log(e)
+ }
+
+ //set true when externalStakingRewards() throws an error
+ baseContractFound = true
+ }
+ }
+
+ if (externalStakingRwdAddresses.length) {
+ multiRwdPools.push({
+ address: fs.stakingAddress,
+ underlyingPool: externalStakingRwdAddresses[0],
+ basePool: externalStakingRwdAddresses[externalStakingRwdAddresses.length - 1],
+ numRewards: rewardsTokens.length,
+ active: isActive,
+ })
+ }
+ })
+ )
+ setMultiRewardPools(multiRwdPools)
+ }, [farmSummaries, library])
+
+ useEffect(() => {
+ call()
+ }, [call])
+
+ return multiRewardPools
+}
+
export const usePairMultiStakingInfo = (
stakingInfo: StakingInfo | undefined,
stakingAddress: string
): StakingInfo | null => {
- const multiRewardPool = multiRewardPools
- .filter((x) => x.address.toLowerCase() === stakingAddress.toLowerCase())
- .find((x) => x.basePool.toLowerCase() === stakingInfo?.poolInfo.poolAddress.toLowerCase())
+ const multiRewardPools = useMultiRewardPools()
+
+ const multiRewardPool = useMemo(() => {
+ return multiRewardPools
+ .filter((x) => x.address.toLowerCase() === stakingAddress.toLowerCase())
+ .find((x) => x.basePool.toLowerCase() === stakingInfo?.poolInfo.poolAddress.toLowerCase())
+ }, [multiRewardPools, stakingAddress, stakingInfo?.poolInfo.poolAddress])
const isTriple = multiRewardPool?.numRewards === 3
diff --git a/src/utils/limitOrder.ts b/src/utils/limitOrder.ts
new file mode 100644
index 00000000..d8d96772
--- /dev/null
+++ b/src/utils/limitOrder.ts
@@ -0,0 +1,29 @@
+const Order = [
+ { name: 'salt', type: 'uint256' },
+ { name: 'makerAsset', type: 'address' },
+ { name: 'takerAsset', type: 'address' },
+ { name: 'maker', type: 'address' },
+ { name: 'receiver', type: 'address' },
+ { name: 'allowedSender', type: 'address' },
+ { name: 'makingAmount', type: 'uint256' },
+ { name: 'takingAmount', type: 'uint256' },
+ { name: 'makerAssetData', type: 'bytes' },
+ { name: 'takerAssetData', type: 'bytes' },
+ { name: 'getMakerAmount', type: 'bytes' },
+ { name: 'getTakerAmount', type: 'bytes' },
+ { name: 'predicate', type: 'bytes' },
+ { name: 'permit', type: 'bytes' },
+ { name: 'interaction', type: 'bytes' },
+]
+
+const name = 'Limit Order Protocol'
+const version = '2'
+
+export function buildOrderData(chainId: string, verifyingContract: string, order: any) {
+ return {
+ primaryType: 'Order',
+ types: { Order },
+ domain: { name, version, chainId, verifyingContract },
+ message: order,
+ }
+}
diff --git a/src/utils/multicall.ts b/src/utils/multicall.ts
new file mode 100644
index 00000000..f11b80e0
--- /dev/null
+++ b/src/utils/multicall.ts
@@ -0,0 +1,18 @@
+import { BytesLike } from 'ethers'
+import { Multicall } from 'generated'
+
+const BUCKET_SIZE = 500
+
+export const multicallBatch = async (
+ multicall: Multicall,
+ calls: { target: string; callData: BytesLike }[],
+ bucketSize = BUCKET_SIZE
+): Promise => {
+ const results = []
+ let i = 0
+ while (i < calls.length) {
+ results.push(...(await multicall.callStatic.aggregate(calls.slice(i, i + bucketSize)).then((r) => r.returnData)))
+ i += bucketSize
+ }
+ return results
+}
diff --git a/yarn.lock b/yarn.lock
index 0158970e..cf230c5a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3968,23 +3968,15 @@
eslint-scope "^5.0.0"
eslint-utils "^2.0.0"
-"@typescript-eslint/parser@^4.28.0", "@typescript-eslint/parser@^4.5.0":
- version "4.32.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.32.0.tgz#751ecca0e2fecd3d44484a9b3049ffc1871616e5"
- integrity sha512-lhtYqQ2iEPV5JqV7K+uOVlPePjClj4dOw7K4/Z1F2yvjIUvyr13yJnDzkK6uon4BjHYuHy3EG0c2Z9jEhFk56w==
- dependencies:
- "@typescript-eslint/scope-manager" "4.32.0"
- "@typescript-eslint/types" "4.32.0"
- "@typescript-eslint/typescript-estree" "4.32.0"
- debug "^4.3.1"
-
-"@typescript-eslint/scope-manager@4.32.0":
- version "4.32.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.32.0.tgz#e03c8668f8b954072b3f944d5b799c0c9225a7d5"
- integrity sha512-DK+fMSHdM216C0OM/KR1lHXjP1CNtVIhJ54kQxfOE6x8UGFAjha8cXgDMBEIYS2XCYjjCtvTkjQYwL3uvGOo0w==
+"@typescript-eslint/parser@^4.33.0", "@typescript-eslint/parser@^4.5.0":
+ version "4.33.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.33.0.tgz#dfe797570d9694e560528d18eecad86c8c744899"
+ integrity sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==
dependencies:
- "@typescript-eslint/types" "4.32.0"
- "@typescript-eslint/visitor-keys" "4.32.0"
+ "@typescript-eslint/scope-manager" "4.33.0"
+ "@typescript-eslint/types" "4.33.0"
+ "@typescript-eslint/typescript-estree" "4.33.0"
+ debug "^4.3.1"
"@typescript-eslint/scope-manager@4.33.0":
version "4.33.0"
@@ -3999,11 +3991,6 @@
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.10.1.tgz#1d7463fa7c32d8a23ab508a803ca2fe26e758727"
integrity sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==
-"@typescript-eslint/types@4.32.0":
- version "4.32.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.32.0.tgz#52c633c18da47aee09449144bf59565ab36df00d"
- integrity sha512-LE7Z7BAv0E2UvqzogssGf1x7GPpUalgG07nGCBYb1oK4mFsOiFC/VrSMKbZQzFJdN2JL5XYmsx7C7FX9p9ns0w==
-
"@typescript-eslint/types@4.33.0":
version "4.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72"
@@ -4023,19 +4010,6 @@
semver "^7.3.2"
tsutils "^3.17.1"
-"@typescript-eslint/typescript-estree@4.32.0":
- version "4.32.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.32.0.tgz#db00ccc41ccedc8d7367ea3f50c6994b8efa9f3b"
- integrity sha512-tRYCgJ3g1UjMw1cGG8Yn1KzOzNlQ6u1h9AmEtPhb5V5a1TmiHWcRyF/Ic+91M4f43QeChyYlVTcf3DvDTZR9vw==
- dependencies:
- "@typescript-eslint/types" "4.32.0"
- "@typescript-eslint/visitor-keys" "4.32.0"
- debug "^4.3.1"
- globby "^11.0.3"
- is-glob "^4.0.1"
- semver "^7.3.5"
- tsutils "^3.21.0"
-
"@typescript-eslint/typescript-estree@4.33.0":
version "4.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609"
@@ -4056,14 +4030,6 @@
dependencies:
eslint-visitor-keys "^1.1.0"
-"@typescript-eslint/visitor-keys@4.32.0":
- version "4.32.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.32.0.tgz#455ba8b51242f2722a497ffae29313f33b14cb7f"
- integrity sha512-e7NE0qz8W+atzv3Cy9qaQ7BTLwWsm084Z0c4nIO2l3Bp6u9WIgdqCgyPyV5oSPDMIW3b20H59OOCmVk3jw3Ptw==
- dependencies:
- "@typescript-eslint/types" "4.32.0"
- eslint-visitor-keys "^2.0.0"
-
"@typescript-eslint/visitor-keys@4.33.0":
version "4.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd"
@@ -4098,10 +4064,10 @@
dependencies:
openzeppelin-solidity "^4.0.0"
-"@ubeswap/sdk@^2.1.1":
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/@ubeswap/sdk/-/sdk-2.1.1.tgz#b1622f66087678a17bdc184256618418dc508732"
- integrity sha512-UBcqcUv2q1IA/5RqYSRHGQiHkm55nN+dZ+b98Il6wsi+FcUS2qyZFPgoNtGD8jpGfauERFJs4OIaRJWJRF1NYg==
+"@ubeswap/sdk@^2.1.2":
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/@ubeswap/sdk/-/sdk-2.1.2.tgz#e0dd4e44c1b9071668f493e8b35486aba694b198"
+ integrity sha512-3K98FW33lGH1K2qAy4KAze7rt5aJ1p+kRkIPNCXsnuRGjRlM38igFcDRggk1pBu3QJel42qyydTQrSXf/gkkIQ==
dependencies:
"@uniswap/v2-core" "^1.0.0"
big.js "^6.0.3"