Commit 2a6b1e63 authored by ian-jh's avatar ian-jh

add initial changes

parent 0bb6bdcd
REACT_APP_NETWORK_ID="1"
REACT_APP_NETWORK_URL=""
\ No newline at end of file
......@@ -80,5 +80,6 @@
"symbol": "Symbol",
"decimals": "Decimals",
"enterTokenCont": "Enter a token address to continue",
"priceChange": "This trade will cause the price to change by"
"priceChange": "Expected price slippage",
"forAtLeast" : "for at least "
}
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="8" cy="8" r="8" fill="#E1E1E1"/>
<path d="M7.09618 9.67828H8.18831V9.60573C8.20358 8.72745 8.45561 8.33413 9.10477 7.93317C9.78831 7.52076 10.2084 6.94033 10.2084 6.08115C10.2084 4.8401 9.26897 4 7.86754 4C6.58067 4 5.54964 4.75227 5.5 6.12697H6.66086C6.70668 5.31742 7.28329 4.96229 7.86754 4.96229C8.51671 4.96229 9.04368 5.39379 9.04368 6.06969C9.04368 6.63866 8.68854 7.03962 8.23413 7.3222C7.52387 7.75752 7.10382 8.18902 7.09618 9.60573V9.67828ZM7.67279 12C8.08902 12 8.43652 11.6601 8.43652 11.2363C8.43652 10.82 8.08902 10.4764 7.67279 10.4764C7.25274 10.4764 6.90907 10.82 6.90907 11.2363C6.90907 11.6601 7.25274 12 7.67279 12Z" fill="#737373"/>
</svg>
......@@ -92,10 +92,10 @@ export default function ContextualInfo({
renderTransactionDetails = () => {},
isError = false,
slippageWarning,
highSlippageWarning
highSlippageWarning,
dropDownContent
}) {
const [showDetails, setShowDetails] = useState(false)
return !allowExpand ? (
<SummaryWrapper>{contextualInfo}</SummaryWrapper>
) : (
......@@ -117,7 +117,7 @@ export default function ContextualInfo({
)}
</>
</SummaryWrapperContainer>
{showDetails && <Details>{renderTransactionDetails()}</Details>}
{showDetails && <Details>{dropDownContent()}</Details>}
</>
)
}
......@@ -6,7 +6,7 @@ import '@reach/dialog/styles.css'
const AnimatedDialogOverlay = animated(DialogOverlay)
const StyledDialogOverlay = styled(AnimatedDialogOverlay).attrs({
suppressClassNameWarning: true
suppressclassnamewarning: 'true'
})`
&[data-reach-dialog-overlay] {
z-index: 2;
......
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import { amountFormatter } from '../../utils'
import NewContextualInfo from '../../components/ContextualInfoNew'
const Flex = styled.div`
display: flex;
justify-content: center;
padding: 14px 0;
button {
max-width: 20rem;
}
`
const SlippageRow = styled(Flex)`
flex-direction: row;
width: 100%;
justify-content: flex-start;
font-size: 0.8rem;
padding: 0;
height: 24px;
margin-bottom: 14px;
`
const Option = styled(Flex)`
align-items: center;
min-width: 55px;
margin-right: 4px;
border-radius: 36px;
border: 1px solid #f2f2f2;
${({ active }) =>
active &&
`
background-color: #2f80ed;
color: white;
border: 1px solid #2f80ed;
`}
&:hover {
cursor: pointer;
}
`
const Input = styled.input`
width: 123.27px;
background: #ffffff;
height: 2rem;
outline: none;
margin-left: 20px;
border: 1px solid #f2f2f2;
box-sizing: border-box;
border-radius: 36px;
color: #aeaeae;
&:focus {
}
text-align: left;
padding-left: 0.9rem;
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
-webkit-appearance: none;
}
${({ active }) =>
active &&
`
border: 1px solid #2f80ed;
text-align: right;
padding-right 1.5rem;
padding-left 0rem;
color : inherit;
`}
${({ warning }) =>
warning &&
`
color : #FF6871;
border: 1px solid #FF6871;
`}
`
const BottomError = styled.div`
margin-top: 1rem;
color: #aeaeae;
${({ warning }) =>
warning &&
`
color : #FF6871;
`}
`
const OptionLarge = styled(Option)`
width: 120px;
`
const Bold = styled.span`
font-weight: 500;
`
const LastSummaryText = styled.div`
margin-top: 0.6rem;
`
const SlippageSelector = styled.div`
margin-top: 28px;
`
const InputGroup = styled.div`
position: relative;
`
const Percent = styled.div`
right: 14px;
top: 8px;
position: absolute;
color: inherit;
font-size: 0, 8rem;
${({ color }) =>
(color === 'faded' &&
`
color : #AEAEAE
`) ||
(color === 'red' &&
`
color : #FF6871
`)}
`
const Faded = styled.span`
opacity: 0.7;
`
const ErrorEmoji = styled.span`
left: 30px;
top: 4px;
position: absolute;
`
export default function TransactionDetails(props) {
const { t } = useTranslation()
function renderSummary() {
let contextualInfo = ''
let isError = false
if (props.inputError || props.independentError) {
contextualInfo = props.inputError || props.independentError
isError = true
} else if (!props.inputCurrency || !props.outputCurrency) {
contextualInfo = t('selectTokenCont')
} else if (!props.independentValue) {
contextualInfo = t('enterValueCont')
} else if (!props.account) {
contextualInfo = t('noWallet')
isError = true
}
const slippageWarningText = props.highSlippageWarning
? t('highSlippageWarning')
: props.slippageWarning
? t('slippageWarning')
: ''
return (
<NewContextualInfo
openDetailsText={t('transactionDetails')}
closeDetailsText={t('hideDetails')}
contextualInfo={contextualInfo ? contextualInfo : slippageWarningText}
allowExpand={
!!(props.inputCurrency && props.outputCurrency && props.inputValueParsed && props.outputValueParsed)
}
isError={isError}
slippageWarning={props.slippageWarning && !contextualInfo}
highSlippageWarning={props.highSlippageWarning && !contextualInfo}
renderTransactionDetails={renderTransactionDetails}
dropDownContent={dropDownContent}
/>
)
}
const [activeIndex, setActiveIndex] = useState(3)
const [placeHolder, setplaceHolder] = useState('Custom')
const [warningType, setWarningType] = useState('none')
const dropDownContent = () => {
return (
<>
{renderTransactionDetails()}
<SlippageSelector>
<SlippageRow>Limit addtional price slippage</SlippageRow>
<SlippageRow>
<Option
onClick={() => {
checkAcceptablePercentValue(0.1)
setActiveIndex(1)
setplaceHolder('Custom')
}}
active={activeIndex === 1 ? true : false}
>
0.1%
</Option>
<Option
onClick={() => {
checkAcceptablePercentValue(1)
setActiveIndex(2)
setplaceHolder('Custom')
}}
active={activeIndex === 2 ? true : false}
>
1%
</Option>
<OptionLarge
onClick={() => {
checkAcceptablePercentValue(2)
setActiveIndex(3)
setplaceHolder('Custom')
}}
active={activeIndex === 3 ? true : false}
>
2%
<Faded>(suggested)</Faded>
</OptionLarge>
<InputGroup>
{warningType !== 'none' ? <ErrorEmoji>⚠️</ErrorEmoji> : ''}
<Input
placeholder={placeHolder}
value={userInput || ''}
onChange={parseInput}
onClick={e => {
setActiveIndex(4)
setplaceHolder('')
parseInput(e)
}}
active={activeIndex === 4 ? true : false}
warning={warningType !== 'none'}
/>
<Percent color={warningType !== 'none' ? 'red' : activeIndex !== 4 ? 'faded' : ''}>%</Percent>
</InputGroup>
</SlippageRow>
<SlippageRow>
<BottomError warning={warningType !== 'none'}>
{warningType === 'invalidEntry' ? 'Please input a valid percentage' : ''}
{warningType === 'invalidEntryBound' ? 'Only choose between 0% and 50%' : ''}
{warningType === 'riskyEntry' ? 'Youre at risk of being front-run ' : ''}
</BottomError>
</SlippageRow>
</SlippageSelector>
</>
)
}
const [userInput, setUserInput] = useState()
const parseInput = e => {
let input = e.target.value
//check for decimal
var isValid = /^[+]?\d*\.?\d{1,2}$/.test(input) || /^[+]?\d*\.$/.test(input)
var decimalLimit = /^\d+\.?\d{0,2}$/.test(input) || input === ''
if (decimalLimit) {
setUserInput(input)
} else {
return
}
if (isValid) {
checkAcceptablePercentValue(input)
} else {
setWarningType('invalidEntry')
}
}
const checkAcceptablePercentValue = input => {
setWarningType('none')
if (input < 0 || input > 50) {
return setWarningType('invalidEntryBound')
}
if (input >= 0 && input < 0.1) {
setWarningType('riskyEntry')
}
let num = parseFloat((input * 100).toFixed(2))
props.setRawSlippage(num)
props.setRawTokenSlippage(num)
}
const b = text => <Bold>{text}</Bold>
const renderTransactionDetails = () => {
if (props.independentField === props.INPUT) {
return (
<div>
<div>
{t('youAreSelling')}{' '}
{b(
`${amountFormatter(
props.independentValueParsed,
props.independentDecimals,
Math.min(4, props.independentDecimals)
)} ${props.inputSymbol}`
)}{' '}
{t('forAtLeast')}
{b(
`${amountFormatter(
props.dependentValueMinumum,
props.dependentDecimals,
Math.min(4, props.dependentDecimals)
)} ${props.outputSymbol}`
)}
.
</div>
<LastSummaryText>
{t('priceChange')} {b(`${props.percentSlippageFormatted}%`)}.
</LastSummaryText>
</div>
)
} else {
return (
<div>
<div>
{t('youAreBuying')}{' '}
{b(
`${amountFormatter(
props.independentValueParsed,
props.independentDecimals,
Math.min(4, props.independentDecimals)
)} ${props.outputSymbol}`
)}
.
</div>
<LastSummaryText>
{t('itWillCost')}{' '}
{b(
`${amountFormatter(
props.dependentValueMaximum,
props.dependentDecimals,
Math.min(4, props.dependentDecimals)
)} ${props.inputSymbol}`
)}{' '}
{t('orTransFail')}
</LastSummaryText>
<LastSummaryText>
{t('priceChange')} {b(`${props.percentSlippageFormatted}%`)}.
</LastSummaryText>
</div>
)
}
}
return <>{renderSummary()}</>
}
......@@ -13,7 +13,7 @@ i18next
},
react: {
useSuspense: true
},
},
fallbackLng: 'en',
preload: ['en'],
keySeparator: false,
......
......@@ -9,6 +9,7 @@ import { Button } from '../../theme'
import CurrencyInputPanel from '../../components/CurrencyInputPanel'
import NewContextualInfo from '../../components/ContextualInfoNew'
import OversizedPanel from '../../components/OversizedPanel'
import TransactionDetails from '../../components/TransactionDetails'
import ArrowDownBlue from '../../assets/images/arrow-down-blue.svg'
import ArrowDownGrey from '../../assets/images/arrow-down-grey.svg'
import { amountFormatter, calculateGasMargin } from '../../utils'
......@@ -26,8 +27,8 @@ const TOKEN_TO_ETH = 1
const TOKEN_TO_TOKEN = 2
// denominated in bips
const ALLOWED_SLIPPAGE = ethers.utils.bigNumberify(200)
const TOKEN_ALLOWED_SLIPPAGE = ethers.utils.bigNumberify(400)
const ALLOWED_SLIPPAGE_DEFAULT = 150
const TOKEN_ALLOWED_SLIPPAGE_DEFAULT = 200
// denominated in seconds
const DEADLINE_FROM_NOW = 60 * 15
......@@ -81,9 +82,9 @@ const Flex = styled.div`
}
`
function calculateSlippageBounds(value, token = false) {
function calculateSlippageBounds(value, token = false, tokenAllowedSlippage, allowedSlippage) {
if (value) {
const offset = value.mul(token ? TOKEN_ALLOWED_SLIPPAGE : ALLOWED_SLIPPAGE).div(ethers.utils.bigNumberify(10000))
const offset = value.mul(token ? tokenAllowedSlippage : allowedSlippage).div(ethers.utils.bigNumberify(10000))
const minimum = value.sub(offset)
const maximum = value.add(offset)
return {
......@@ -244,12 +245,18 @@ export default function Swap({ initialCurrency }) {
const addTransaction = useTransactionAdder()
const [rawSlippage, setRawSlippage] = useState(ALLOWED_SLIPPAGE_DEFAULT)
const [rawTokenSlippage, setRawTokenSlippage] = useState(TOKEN_ALLOWED_SLIPPAGE_DEFAULT)
let allowedSlippageBig = ethers.utils.bigNumberify(rawSlippage)
let tokenAllowedSlippageBig = ethers.utils.bigNumberify(rawTokenSlippage)
// analytics
useEffect(() => {
ReactGA.pageview(window.location.pathname + window.location.search)
}, [])
// core swap state
// core swap state-
const [swapState, dispatchSwapState] = useReducer(swapStateReducer, initialCurrency, getInitialSwapState)
const { independentValue, dependentValue, independentField, inputCurrency, outputCurrency } = swapState
......@@ -326,7 +333,9 @@ export default function Swap({ initialCurrency }) {
// calculate slippage from target rate
const { minimum: dependentValueMinumum, maximum: dependentValueMaximum } = calculateSlippageBounds(
dependentValue,
swapType === TOKEN_TO_TOKEN
swapType === TOKEN_TO_TOKEN,
tokenAllowedSlippageBig,
allowedSlippageBig
)
// validate input allowance + balance
......@@ -496,118 +505,6 @@ export default function Swap({ initialCurrency }) {
return `Balance: ${value}`
}
function renderTransactionDetails() {
ReactGA.event({
category: 'TransactionDetail',
action: 'Open'
})
const b = text => <BlueSpan>{text}</BlueSpan>
if (independentField === INPUT) {
return (
<div>
<div>
{t('youAreSelling')}{' '}
{b(
`${amountFormatter(
independentValueParsed,
independentDecimals,
Math.min(4, independentDecimals)
)} ${inputSymbol}`
)}
.
</div>
<LastSummaryText>
{t('youWillReceive')}{' '}
{b(
`${amountFormatter(
dependentValueMinumum,
dependentDecimals,
Math.min(4, dependentDecimals)
)} ${outputSymbol}`
)}{' '}
{t('orTransFail')}
</LastSummaryText>
<LastSummaryText>
{(slippageWarning || highSlippageWarning) && (
<span role="img" aria-label="warning">
⚠️
</span>
)}
{t('priceChange')} {b(`${percentSlippageFormatted}%`)}.
</LastSummaryText>
</div>
)
} else {
return (
<div>
<div>
{t('youAreBuying')}{' '}
{b(
`${amountFormatter(
independentValueParsed,
independentDecimals,
Math.min(4, independentDecimals)
)} ${outputSymbol}`
)}
.
</div>
<LastSummaryText>
{t('itWillCost')}{' '}
{b(
`${amountFormatter(
dependentValueMaximum,
dependentDecimals,
Math.min(4, dependentDecimals)
)} ${inputSymbol}`
)}{' '}
{t('orTransFail')}
</LastSummaryText>
<LastSummaryText>
{t('priceChange')} {b(`${percentSlippageFormatted}%`)}.
</LastSummaryText>
</div>
)
}
}
function renderSummary() {
let contextualInfo = ''
let isError = false
if (inputError || independentError) {
contextualInfo = inputError || independentError
isError = true
} else if (!inputCurrency || !outputCurrency) {
contextualInfo = t('selectTokenCont')
} else if (!independentValue) {
contextualInfo = t('enterValueCont')
} else if (!account) {
contextualInfo = t('noWallet')
isError = true
}
const slippageWarningText = highSlippageWarning
? t('highSlippageWarning')
: slippageWarning
? t('slippageWarning')
: ''
return (
<NewContextualInfo
openDetailsText={t('transactionDetails')}
closeDetailsText={t('hideDetails')}
contextualInfo={contextualInfo ? contextualInfo : slippageWarningText}
allowExpand={!!(inputCurrency && outputCurrency && inputValueParsed && outputValueParsed)}
isError={isError}
slippageWarning={slippageWarning && !contextualInfo}
highSlippageWarning={highSlippageWarning && !contextualInfo}
renderTransactionDetails={renderTransactionDetails}
/>
)
}
async function onSwap() {
const deadline = Math.ceil(Date.now() / 1000) + DEADLINE_FROM_NOW
......@@ -743,7 +640,31 @@ export default function Swap({ initialCurrency }) {
)}
</ExchangeRateWrapper>
</OversizedPanel>
{renderSummary()}
<TransactionDetails
account={account}
setRawSlippage={setRawSlippage}
setRawTokenSlippage={setRawTokenSlippage}
rawSlippage={rawSlippage}
slippageWarning={slippageWarning}
highSlippageWarning={highSlippageWarning}
inputError={inputError}
independentError={independentError}
inputCurrency={inputCurrency}
outputCurrency={outputCurrency}
independentValue={independentValue}
independentValueParsed={independentValueParsed}
independentField={independentField}
INPUT={INPUT}
inputValueParsed={inputValueParsed}
outputValueParsed={outputValueParsed}
inputSymbol={inputSymbol}
outputSymbol={outputSymbol}
dependentValueMinumum={dependentValueMinumum}
dependentValueMaximum={dependentValueMaximum}
dependentDecimals={dependentDecimals}
independentDecimals={independentDecimals}
percentSlippageFormatted={percentSlippageFormatted}
/>
<Flex>
<Button disabled={!isValid} onClick={onSwap} warning={highSlippageWarning}>
{highSlippageWarning ? t('swapAnyway') : t('swap')}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment