diff --git a/app/src/components/common/form/textfield_custom_symbol/index.tsx b/app/src/components/common/form/textfield_custom_symbol/index.tsx index 1edf6f6418..e3e63363be 100644 --- a/app/src/components/common/form/textfield_custom_symbol/index.tsx +++ b/app/src/components/common/form/textfield_custom_symbol/index.tsx @@ -5,6 +5,7 @@ import { CommonDisabledCSS } from '../common_styled' interface Props { disabled?: boolean + error?: boolean formField: any symbol: any shouldDisplayMaxButton?: boolean @@ -12,10 +13,10 @@ interface Props { style?: CSSProperties | undefined } -const FieldWrapper = styled.div` +const FieldWrapper = styled.div<{ error?: boolean }>` align-items: center; background-color: ${props => props.theme.textfield.backgroundColor}; - border-color: ${props => props.theme.textfield.borderColor}; + border-color: ${props => (props.error ? props.theme.alert : props.theme.border1)}; border-style: ${props => props.theme.textfield.borderStyle}; border-width: ${props => props.theme.textfield.borderWidth}; border-radius: ${props => props.theme.textfield.borderRadius}; @@ -26,16 +27,16 @@ const FieldWrapper = styled.div` width: 100%; &:hover { - border-color: ${props => props.theme.textfield.borderColorOnHover}; + border-color: ${props => (props.error ? props.theme.alert : props.theme.border2)}; .btn--max { - border-color: ${props => props.theme.textfield.borderColorOnHover}; + border-color: ${props => (props.error ? props.theme.alert : props.theme.border2)}; } } &:focus-within { - border-color: ${props => props.theme.textfield.borderColorActive}; + border-color: ${props => (props.error ? props.theme.alert : props.theme.border3)}; .btn--max { - border-color: ${props => props.theme.textfield.borderColorActive}; + border-color: ${props => (props.error ? props.theme.alert : props.theme.border3)}; } } @@ -44,12 +45,12 @@ const FieldWrapper = styled.div` > input { background-color: transparent; border: none; - color: ${props => props.theme.textfield.color}; + color: ${props => props.theme.text2}; flex-grow: 1; font-family: ${props => props.theme.fonts.fontFamily}; font-size: ${props => props.theme.textfield.fontSize}; - font-weight: ${props => props.theme.textfield.fontWeight}; - line-height: 1.2; + font-weight: 400; + line-height: 18px; margin: 0 5px 0 0; min-width: 0; outline: ${props => props.theme.textfield.outline}; @@ -91,7 +92,7 @@ const FieldWrapper = styled.div` ` const Symbol = styled.span<{ marginRight?: boolean }>` - color: ${props => props.theme.colors.primary}; + color: ${props => props.theme.text2}; flex-shrink: 0; font-size: 14px; font-weight: 500; @@ -124,7 +125,6 @@ const MaxButton = styled.span` export const TextfieldCustomSymbol = (props: Props) => { const { disabled, formField, onClickMaxButton, shouldDisplayMaxButton, symbol, ...restProps } = props - // eslint-disable-next-line no-warning-comments //TODO: use a input[text] instead of passing a return ( diff --git a/app/src/components/market/common_sections/message_text/warning_message/index.tsx b/app/src/components/market/common_sections/message_text/warning_message/index.tsx index 0b9a0c0179..d4837d204c 100644 --- a/app/src/components/market/common_sections/message_text/warning_message/index.tsx +++ b/app/src/components/market/common_sections/message_text/warning_message/index.tsx @@ -32,7 +32,7 @@ const Description = styled.p` width: 100%; &.danger { - color: ${props => props.theme.message.colors.error}; + color: ${props => props.theme.alert}; } ` diff --git a/app/src/components/market/common_styled/index.tsx b/app/src/components/market/common_styled/index.tsx index 64935c9236..2a39d17031 100644 --- a/app/src/components/market/common_styled/index.tsx +++ b/app/src/components/market/common_styled/index.tsx @@ -168,7 +168,7 @@ export const ErrorsWrapper = styled.div` ` export const GenericError = styled.p<{ margin?: string }>` - color: ${props => props.theme.colors.error}; + color: ${props => props.theme.alert}; font-size: 13px; font-weight: 500; line-height: 1.5; diff --git a/app/src/components/market/market_buy/market_buy.tsx b/app/src/components/market/market_buy/market_buy.tsx index 05da44ea5a..591adb5d67 100644 --- a/app/src/components/market/market_buy/market_buy.tsx +++ b/app/src/components/market/market_buy/market_buy.tsx @@ -205,6 +205,7 @@ const MarketBuyWrapper: React.FC = (props: Props) => { { = (props: Props) => { const { proxyIsUpToDate, updateProxy } = useCpkProxy(collateral.address === pseudoNativeAssetAddress) const isUpdated = RemoteData.hasData(proxyIsUpToDate) ? proxyIsUpToDate.data : true + const { relayFeeGreaterThanAmount, relayFeeGreaterThanBalance } = useRelay(amount || new BigNumber(0), collateral) + useEffect(() => { dispatch(fetchAccountBalance(account, provider, collateral)) }, [dispatch, account, provider, collateral]) @@ -285,10 +287,14 @@ const FundingAndFeeStep: React.FC = (props: Props) => { ? null : !maybeCollateralBalance.eq(collateralBalance) ? null + : relayFeeGreaterThanBalance + ? 'Insufficient Dai in your Omen Account' : maybeCollateralBalance.isZero() && funding.gt(maybeCollateralBalance) ? `Insufficient balance` : funding.gt(maybeCollateralBalance) ? `Value must be less than or equal to ${collateralBalanceFormatted} ${collateral.symbol}` + : relayFeeGreaterThanAmount + ? 'Relay fee is greater than buy amount' : null const isCreateMarketbuttonDisabled = @@ -472,6 +478,7 @@ const FundingAndFeeStep: React.FC = (props: Props) => { /> = (props: Props) => { = (props: Props) => { { { { shouldDisplayMaxButton symbol="Shares" /> - {sharesAmountError && {sharesAmountError}} )} diff --git a/app/src/components/market/market_sell/market_sell.tsx b/app/src/components/market/market_sell/market_sell.tsx index 0739551f74..caffa5651a 100644 --- a/app/src/components/market/market_sell/market_sell.tsx +++ b/app/src/components/market/market_sell/market_sell.tsx @@ -98,6 +98,7 @@ const MarketSellWrapper: React.FC = (props: Props) => { { /> { + const { account, balances, networkId, relay } = useConnectedWeb3Context() + + const [relayAddress, setRelayAddress] = useState('') + const [relayFee, setRelayFee] = useState(new BigNumber('0')) + const [relayFeeGreaterThanAmount, setRelayFeeGreaterThanAmount] = useState(false) + const [relayFeeGreaterThanBalance, setRelayFeeGreaterThanBalance] = useState(false) + + const fetchRelayInfo = async (status?: Status) => { + const relayService = new RelayService() + const { address, fee } = await relayService.getInfo() + if (!status || status.active) { + setRelayAddress(address) + + const feeBN = new BigNumber(fee) + setRelayFee(feeBN) + setRelayFeeGreaterThanBalance(feeBN.gt(balances.xDaiBalance)) + } + } + + useEffect(() => { + const status = { active: true } + if (account && relay) { + fetchRelayInfo(status) + } else { + setRelayAddress('') + setRelayFee(new BigNumber('0')) + setRelayFeeGreaterThanBalance(false) + } + return () => { + status.active = false + } + // eslint-disable-next-line + }, [account, networkId, relay]) + + useEffect(() => { + if (account && relay && collateral && amount) { + const native = getNativeAsset(networkId, relay) + const feeGreaterThanAmount = + relay && amount && !amount.isZero() && relayFee.gt(amount) && collateral.address === native.address + setRelayFeeGreaterThanAmount(feeGreaterThanAmount) + } else { + setRelayFeeGreaterThanAmount(false) + } + }, [amount, collateral, account, relay, networkId, relayFee]) + + return { relayAddress, relayFee, relayFeeGreaterThanAmount, relayFeeGreaterThanBalance } +} diff --git a/app/src/pages/market_sections/market_buy_container.tsx b/app/src/pages/market_sections/market_buy_container.tsx index e21c5254cc..cc8a99c91d 100644 --- a/app/src/pages/market_sections/market_buy_container.tsx +++ b/app/src/pages/market_sections/market_buy_container.tsx @@ -6,7 +6,14 @@ import { STANDARD_DECIMALS } from '../../common/constants' import { MarketBuy } from '../../components/market/market_buy/market_buy' import { ScalarMarketBuy } from '../../components/market/market_buy/scalar_market_buy' import { ConnectedWeb3Context, useConnectedWeb3Context } from '../../contexts' -import { useAsyncDerivedValue, useCollateralBalance, useContracts, useCpkAllowance, useCpkProxy } from '../../hooks' +import { + useAsyncDerivedValue, + useCollateralBalance, + useContracts, + useCpkAllowance, + useCpkProxy, + useRelay, +} from '../../hooks' import { CPKService, MarketMakerService } from '../../services' import { getNativeAsset, pseudoNativeAssetAddress } from '../../util/networks' import { RemoteData } from '../../util/remote_data' @@ -118,6 +125,8 @@ const MarketBuyContainer: React.FC = (props: Props) => { context, ) + const { relayFeeGreaterThanAmount, relayFeeGreaterThanBalance } = useRelay(amount, collateral) + useEffect(() => { setIsNegativeAmount((amount || Zero).lt(Zero)) }, [amount, collateral.decimals]) @@ -201,10 +210,14 @@ const MarketBuyContainer: React.FC = (props: Props) => { ? null : maybeCollateralBalance === null ? null + : relayFeeGreaterThanBalance + ? 'Insufficient Dai in your Omen Account' : maybeCollateralBalance.isZero() && amount?.gt(maybeCollateralBalance) ? `Insufficient balance` : amount?.gt(maybeCollateralBalance) ? `Value must be less than or equal to ${currentBalance} ${collateral.symbol}` + : relayFeeGreaterThanAmount + ? 'Relay fee is greater than buy amount' : null const unlockCollateral = async () => { @@ -245,7 +258,9 @@ const MarketBuyContainer: React.FC = (props: Props) => { hasEnoughAllowance !== Ternary.True) || amountError !== null || isNegativeAmount || - !isUpdated + !isUpdated || + relayFeeGreaterThanAmount || + relayFeeGreaterThanBalance const shouldDisplayMaxButton = collateral.address !== pseudoNativeAssetAddress const sharesTotal = bigNumberToString(tradedShares, collateral.decimals) diff --git a/app/src/pages/market_sections/market_pool_liquidity_container.tsx b/app/src/pages/market_sections/market_pool_liquidity_container.tsx index 86e7c51ab8..d86ef91bfe 100644 --- a/app/src/pages/market_sections/market_pool_liquidity_container.tsx +++ b/app/src/pages/market_sections/market_pool_liquidity_container.tsx @@ -13,6 +13,7 @@ import { useCpkProxy, useFundingBalance, useGraphLiquidityMiningCampaigns, + useRelay, } from '../../hooks' import { GraphResponseLiquidityMiningCampaign } from '../../hooks/useGraphLiquidityMiningCampaigns' import { useTokenPrice } from '../../hooks/useTokenPrice' @@ -179,6 +180,11 @@ const MarketPoolLiquidityContainer: React.FC = (props: Props) => { const totalUserLiquidity = totalDepositedTokens.add(userEarnings).add(userStakedTokens) + const { relayFeeGreaterThanAmount, relayFeeGreaterThanBalance } = useRelay( + amountToFund || new BigNumber(0), + collateral, + ) + const { proxyIsUpToDate, updateProxy } = useCpkProxy() const isUpdated = RemoteData.hasData(proxyIsUpToDate) ? proxyIsUpToDate.data : true @@ -186,16 +192,22 @@ const MarketPoolLiquidityContainer: React.FC = (props: Props) => { ? null : maybeCollateralBalance === null ? null + : relayFeeGreaterThanBalance + ? 'Insufficient Dai in your Omen Account' : maybeCollateralBalance.isZero() && amountToFund?.gt(maybeCollateralBalance) ? `Insufficient balance` : amountToFund?.gt(maybeCollateralBalance) ? `Value must be less than or equal to ${walletBalance} ${collateral.symbol}` + : relayFeeGreaterThanAmount + ? 'Relay fee is greater than buy amount' : null const sharesAmountError = isTransactionProcessing ? null : maybeFundingBalance === null ? null + : relayFeeGreaterThanBalance + ? 'Insufficient Dai in your Omen Account' : maybeFundingBalance.isZero() && amountToRemove?.gt(maybeFundingBalance) && amountToRemove?.gt(userStakedTokens) ? `Insufficient balance` : amountToRemove?.gt(fundingBalance) && amountToRemove?.gt(userStakedTokens) @@ -215,7 +227,8 @@ const MarketPoolLiquidityContainer: React.FC = (props: Props) => { (!cpk?.isSafeApp && collateral.address !== pseudoNativeAssetAddress && hasEnoughAllowance !== Ternary.True) || collateralAmountError !== null || currentDate > resolutionDate || - isNegativeAmountToFund + isNegativeAmountToFund || + relayFeeGreaterThanAmount const upgradeProxy = async () => { if (!cpk) { diff --git a/app/src/pages/market_sections/market_sell_container.tsx b/app/src/pages/market_sections/market_sell_container.tsx index 608f4bfdca..cb6a61139f 100644 --- a/app/src/pages/market_sections/market_sell_container.tsx +++ b/app/src/pages/market_sections/market_sell_container.tsx @@ -6,7 +6,7 @@ import { STANDARD_DECIMALS } from '../../common/constants' import { MarketSell } from '../../components/market/market_sell/market_sell' import { ScalarMarketSell } from '../../components/market/market_sell/scalar_market_sell' import { useConnectedWeb3Context } from '../../contexts' -import { useAsyncDerivedValue, useContracts } from '../../hooks' +import { useAsyncDerivedValue, useContracts, useRelay } from '../../hooks' import { MarketMakerService } from '../../services' import { getLogger } from '../../util/logger' import { @@ -81,6 +81,8 @@ const MarketSellContainer: React.FC = (props: Props) => { const [amountSharesToDisplay, setAmountSharesToDisplay] = useState('') const [message, setMessage] = useState('') + const { relayFeeGreaterThanBalance } = useRelay() + let defaultOutcomeIndex = 0 for (let i = 0; i < balances.length; i++) { const shares = bigNumberToNumber(balances[i].shares, collateral.decimals) @@ -122,6 +124,8 @@ const MarketSellContainer: React.FC = (props: Props) => { ? null : balanceItem.shares === null ? null + : relayFeeGreaterThanBalance + ? 'Insufficient Dai in your Omen Account' : balanceItem.shares.isZero() && amountShares?.gt(balanceItem.shares) ? `Insufficient balance` : amountShares?.gt(balanceItem.shares) diff --git a/app/src/services/cpk/fns.ts b/app/src/services/cpk/fns.ts index 7d9e58c81b..7905bf2ba4 100644 --- a/app/src/services/cpk/fns.ts +++ b/app/src/services/cpk/fns.ts @@ -105,14 +105,22 @@ export const exec = async (params: ExecParams) => { */ interface FeeParams { - service: CPKService amount: BigNumber + collateral: Token + service: CPKService + native: Token } export const fee = async (params: FeeParams) => { - const { service } = params - const amount = await service.subRelayFee(params.amount) - return { ...params, amount } + const { collateral, native, service } = params + if (service.cpk.relay && collateral.address === native.address) { + const amount = await service.subRelayFee(params.amount) + if (amount.lte(0)) { + throw new Error('Purchase amount is too small') + } + return { ...params, amount } + } + return params } /** diff --git a/app/src/services/index.tsx b/app/src/services/index.tsx index 8495d5810f..2583e14245 100644 --- a/app/src/services/index.tsx +++ b/app/src/services/index.tsx @@ -11,3 +11,4 @@ export { OvmService } from './ovm' export { XdaiService } from './xdai' export { AirdropService } from './airdrop' export { OmenGuildService } from './guild' +export { RelayService } from './relay'