Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
I
interface
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
LuckySwap
interface
Commits
30887ef1
Commit
30887ef1
authored
Jul 26, 2019
by
Ian Lapham
Committed by
Noah Zinsmeister
Jul 26, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add slippage option (#369)
* add custom slippage to /swap
parent
12344f74
Changes
12
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
647 additions
and
145 deletions
+647
-145
en.json
public/locales/en.json
+2
-1
question-mark.svg
src/assets/images/question-mark.svg
+4
-0
question.svg
src/assets/images/question.svg
+4
-0
index.js
src/components/AddressInputPanel/index.js
+1
-1
index.js
src/components/ContextualInfoNew/index.js
+5
-6
index.js
src/components/CurrencyInputPanel/index.js
+3
-3
index.js
src/components/NavigationTabs/index.js
+2
-2
index.js
src/components/TransactionDetails/index.js
+571
-0
ModeSelector.js
src/pages/Pool/ModeSelector.js
+1
-1
index.js
src/pages/Send/index.js
+1
-1
index.js
src/pages/Swap/index.js
+49
-130
index.js
src/theme/index.js
+4
-0
No files found.
public/locales/en.json
View file @
30887ef1
...
@@ -80,5 +80,6 @@
...
@@ -80,5 +80,6 @@
"symbol"
:
"Symbol"
,
"symbol"
:
"Symbol"
,
"decimals"
:
"Decimals"
,
"decimals"
:
"Decimals"
,
"enterTokenCont"
:
"Enter a token address to continue"
,
"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 "
}
}
src/assets/images/question-mark.svg
0 → 100644
View file @
30887ef1
<svg
width=
"18"
height=
"18"
viewBox=
"0 0 18 18"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<circle
cx=
"9"
cy=
"9"
r=
"9"
fill=
"#E1E1E1"
/>
<path
d=
"M8.06493 10.8317H9.15706V10.7592C9.17233 9.88089 9.42436 9.48757 10.0735 9.08662C10.7571 8.67421 11.1771 8.09378 11.1771 7.23459C11.1771 5.99354 10.2377 5.15344 8.83629 5.15344C7.54942 5.15344 6.51839 5.90571 6.46875 7.28041H7.62961C7.67543 6.47086 8.25204 6.11573 8.83629 6.11573C9.48546 6.11573 10.0124 6.54724 10.0124 7.22313C10.0124 7.79211 9.65729 8.19306 9.20288 8.47564C8.49262 8.91096 8.07257 9.34246 8.06493 10.7592V10.8317ZM8.64154 13.1534C9.05777 13.1534 9.40527 12.8136 9.40527 12.3897C9.40527 11.9735 9.05777 11.6298 8.64154 11.6298C8.22149 11.6298 7.87782 11.9735 7.87782 12.3897C7.87782 12.8136 8.22149 13.1534 8.64154 13.1534Z"
fill=
"#737373"
/>
</svg>
src/assets/images/question.svg
0 → 100644
View file @
30887ef1
<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>
src/components/AddressInputPanel/index.js
View file @
30887ef1
...
@@ -21,7 +21,7 @@ const ContainerRow = styled.div`
...
@@ -21,7 +21,7 @@ const ContainerRow = styled.div`
justify-content: center;
justify-content: center;
align-items: center;
align-items: center;
border-radius: 1.25rem;
border-radius: 1.25rem;
box-shadow: 0 0 0
0.5
px
${({
error
,
theme
})
=>
(
error
?
theme
.
salmonRed
:
theme
.
mercuryGray
)}
;
box-shadow: 0 0 0
1
px
${({
error
,
theme
})
=>
(
error
?
theme
.
salmonRed
:
theme
.
mercuryGray
)}
;
background-color:
${({
theme
})
=>
theme
.
white
}
;
background-color:
${({
theme
})
=>
theme
.
white
}
;
transition: box-shadow 200ms ease-in-out;
transition: box-shadow 200ms ease-in-out;
`
`
...
...
src/components/ContextualInfoNew/index.js
View file @
30887ef1
...
@@ -32,10 +32,10 @@ const SummaryWrapperContainer = styled.div`
...
@@ -32,10 +32,10 @@ const SummaryWrapperContainer = styled.div`
const
Details
=
styled
.
div
`
const
Details
=
styled
.
div
`
background-color:
${({
theme
})
=>
theme
.
concreteGray
}
;
background-color:
${({
theme
})
=>
theme
.
concreteGray
}
;
padding: 1.5rem;
/* padding: 1.25rem 1.25rem 1rem 1.25rem; */
border-radius: 1rem;
border-radius: 1rem;
font-size: 0.75rem;
font-size: 0.75rem;
margin
-top: 1
rem;
margin
: 1rem 0.5rem 0 0.5
rem;
`
`
const
ErrorSpan
=
styled
.
span
`
const
ErrorSpan
=
styled
.
span
`
...
@@ -89,13 +89,12 @@ export default function ContextualInfo({
...
@@ -89,13 +89,12 @@ export default function ContextualInfo({
closeDetailsText
=
'
Hide Details
'
,
closeDetailsText
=
'
Hide Details
'
,
contextualInfo
=
''
,
contextualInfo
=
''
,
allowExpand
=
false
,
allowExpand
=
false
,
renderTransactionDetails
=
()
=>
{},
isError
=
false
,
isError
=
false
,
slippageWarning
,
slippageWarning
,
highSlippageWarning
highSlippageWarning
,
dropDownContent
})
{
})
{
const
[
showDetails
,
setShowDetails
]
=
useState
(
false
)
const
[
showDetails
,
setShowDetails
]
=
useState
(
false
)
return
!
allowExpand
?
(
return
!
allowExpand
?
(
<
SummaryWrapper
>
{
contextualInfo
}
<
/SummaryWrapper
>
<
SummaryWrapper
>
{
contextualInfo
}
<
/SummaryWrapper
>
)
:
(
)
:
(
...
@@ -117,7 +116,7 @@ export default function ContextualInfo({
...
@@ -117,7 +116,7 @@ export default function ContextualInfo({
)}
)}
<
/
>
<
/
>
<
/SummaryWrapperContainer
>
<
/SummaryWrapperContainer
>
{
showDetails
&&
<
Details
>
{
renderTransactionDetails
()}
<
/Details>
}
{
showDetails
&&
<
Details
>
{
dropDownContent
()}
<
/Details>
}
<
/
>
<
/
>
)
)
}
}
src/components/CurrencyInputPanel/index.js
View file @
30887ef1
...
@@ -70,7 +70,7 @@ const CurrencySelect = styled.button`
...
@@ -70,7 +70,7 @@ const CurrencySelect = styled.button`
}
}
:focus {
:focus {
box-shadow: 0 0
0.5px 0.5
px
${({
theme
})
=>
theme
.
malibuBlue
}
;
box-shadow: 0 0
1px 1
px
${({
theme
})
=>
theme
.
malibuBlue
}
;
}
}
:active {
:active {
...
@@ -104,12 +104,12 @@ const InputPanel = styled.div`
...
@@ -104,12 +104,12 @@ const InputPanel = styled.div`
const
Container
=
styled
.
div
`
const
Container
=
styled
.
div
`
border-radius: 1.25rem;
border-radius: 1.25rem;
box-shadow: 0 0 0
0.5
px
${({
error
,
theme
})
=>
(
error
?
theme
.
salmonRed
:
theme
.
mercuryGray
)}
;
box-shadow: 0 0 0
1
px
${({
error
,
theme
})
=>
(
error
?
theme
.
salmonRed
:
theme
.
mercuryGray
)}
;
background-color:
${({
theme
})
=>
theme
.
white
}
;
background-color:
${({
theme
})
=>
theme
.
white
}
;
transition: box-shadow 200ms ease-in-out;
transition: box-shadow 200ms ease-in-out;
:focus-within {
:focus-within {
box-shadow: 0 0
0.5px 0.5
px
${({
theme
})
=>
theme
.
malibuBlue
}
;
box-shadow: 0 0
1px 1
px
${({
theme
})
=>
theme
.
malibuBlue
}
;
}
}
`
`
...
...
src/components/NavigationTabs/index.js
View file @
30887ef1
...
@@ -84,11 +84,11 @@ const StyledNavLink = styled(NavLink).attrs({
...
@@ -84,11 +84,11 @@ const StyledNavLink = styled(NavLink).attrs({
&.
${
activeClassName
}
{
&.
${
activeClassName
}
{
background-color:
${({
theme
})
=>
theme
.
white
}
;
background-color:
${({
theme
})
=>
theme
.
white
}
;
border-radius: 3rem;
border-radius: 3rem;
box-shadow: 0 0
0.5
px 1px
${({
theme
})
=>
theme
.
mercuryGray
}
;
box-shadow: 0 0
1
px 1px
${({
theme
})
=>
theme
.
mercuryGray
}
;
font-weight: 500;
font-weight: 500;
color:
${({
theme
})
=>
theme
.
royalBlue
}
;
color:
${({
theme
})
=>
theme
.
royalBlue
}
;
:hover {
:hover {
box-shadow: 0 0
0.5
px 1px
${({
theme
})
=>
darken
(
0.1
,
theme
.
mercuryGray
)}
;
box-shadow: 0 0
1
px 1px
${({
theme
})
=>
darken
(
0.1
,
theme
.
mercuryGray
)}
;
}
}
}
}
...
...
src/components/TransactionDetails/index.js
0 → 100644
View file @
30887ef1
import
React
,
{
useState
,
useEffect
,
useRef
}
from
'
react
'
import
{
useTranslation
}
from
'
react-i18next
'
import
styled
,
{
css
,
keyframes
}
from
'
styled-components
'
import
{
transparentize
,
darken
}
from
'
polished
'
import
{
amountFormatter
}
from
'
../../utils
'
import
{
useDebounce
}
from
'
../../hooks
'
import
question
from
'
../../assets/images/question.svg
'
import
NewContextualInfo
from
'
../../components/ContextualInfoNew
'
const
WARNING_TYPE
=
Object
.
freeze
({
none
:
'
none
'
,
emptyInput
:
'
emptyInput
'
,
invalidEntryBound
:
'
invalidEntryBound
'
,
riskyEntryHigh
:
'
riskyEntryHigh
'
,
riskyEntryLow
:
'
riskyEntryLow
'
})
const
b
=
text
=>
<
Bold
>
{
text
}
<
/Bold
>
const
Flex
=
styled
.
div
`
display: flex;
justify-content: center;
`
const
FlexBetween
=
styled
.
div
`
display: flex;
justify-content: space-between;
align-items: center;
height: 100%;
`
const
WrappedSlippageRow
=
({
wrap
,
...
rest
})
=>
<
Flex
{...
rest
}
/
>
const
SlippageRow
=
styled
(
WrappedSlippageRow
)
`
position: relative;
flex-wrap:
${({
wrap
})
=>
wrap
&&
'
wrap
'
}
;
flex-direction: row;
justify-content: flex-start;
align-items: center;
width: 100%;
padding: 0;
padding-top:
${({
wrap
})
=>
wrap
&&
'
0.25rem
'
}
;
`
const
QuestionWrapper
=
styled
.
button
`
display: flex;
align-items: center;
justify-content: center;
margin: 0;
padding: 0;
margin-left: 0.4rem;
padding: 0.2rem;
border: none;
background: none;
outline: none;
cursor: default;
border-radius: 36px;
:hover,
:focus {
opacity: 0.7;
}
`
const
HelpCircleStyled
=
styled
.
img
`
height: 18px;
width: 18px;
`
const
fadeIn
=
keyframes
`
from {
opacity : 0;
}
to {
opacity : 1;
}
`
const
Popup
=
styled
(
Flex
)
`
position: absolute;
width: 228px;
left: -78px;
top: -124px;
flex-direction: column;
align-items: center;
padding: 0.6rem 1rem;
line-height: 150%;
background:
${({
theme
})
=>
theme
.
charcoalBlack
}
;
border-radius: 8px;
animation:
${
fadeIn
}
0.15s linear;
color: white;
font-style: italic;
${({
theme
})
=>
theme
.
mediaWidth
.
upToSmall
`
left: -20px;
`
}
`
const
FancyButton
=
styled
.
button
`
align-items: center;
min-width: 55px;
height: 2rem;
border-radius: 36px;
border: 1px solid
${({
theme
})
=>
theme
.
mercuryGray
}
;
outline: none;
background:
${({
theme
})
=>
theme
.
white
}
;
:hover {
cursor: inherit;
border: 1px solid
${({
theme
})
=>
theme
.
royalBlue
}
;
box-shadow:
${({
theme
})
=>
transparentize
(
0.6
,
theme
.
royalBlue
)}
0px 0px 0px 2px;
}
:focus {
box-shadow:
${({
theme
})
=>
transparentize
(
0.6
,
theme
.
royalBlue
)}
0px 0px 0px 2px;
}
`
const
Option
=
styled
(
FancyButton
)
`
margin-right: 8px;
margin-top: 6px;
${({
active
,
theme
})
=>
active
&&
css
`
background-color:
${({
theme
})
=>
theme
.
royalBlue
}
;
color:
${({
theme
})
=>
theme
.
white
}
;
border: none;
`
}
`
const
OptionLarge
=
styled
(
Option
)
`
width: 120px;
`
const
Input
=
styled
.
input
`
background:
${({
theme
})
=>
theme
.
white
}
;
flex-grow: 1;
outline: none;
box-sizing: border-box;
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
-webkit-appearance: none;
}
cursor: inherit;
color:
${({
theme
})
=>
theme
.
doveGray
}
;
text-align: left;
${({
active
})
=>
active
&&
css
`
color: initial;
cursor: initial;
text-align: right;
`
}
${({
placeholder
})
=>
placeholder
!==
'
Custom
'
&&
css
`
text-align: right;
`
}
${({
color
})
=>
color
===
'
red
'
&&
css
`
color:
${({
theme
})
=>
theme
.
salmonRed
}
;
`
}
`
const
BottomError
=
styled
.
div
`
${({
show
})
=>
show
&&
css
`
padding-top: 12px;
`
}
color:
${({
theme
})
=>
theme
.
doveGray
}
;
${({
color
})
=>
color
===
'
red
'
&&
css
`
color:
${({
theme
})
=>
theme
.
salmonRed
}
;
`
}
`
const
OptionCustom
=
styled
(
FancyButton
)
`
height: 2rem;
position: relative;
width: 120px;
margin-top: 6px;
padding: 0 0.75rem;
${({
active
})
=>
active
&&
css
`
border: 1px solid
${({
theme
})
=>
theme
.
royalBlue
}
;
`
}
${({
color
})
=>
color
===
'
red
'
&&
css
`
border: 1px solid
${({
theme
})
=>
theme
.
salmonRed
}
;
`
}
input {
width: 100%;
height: 100%;
border: 0px;
border-radius: 2rem;
}
`
const
Bold
=
styled
.
span
`
font-weight: 500;
`
const
LastSummaryText
=
styled
.
div
`
margin-top: 0.6rem;
`
const
SlippageSelector
=
styled
.
div
`
background-color:
${({
theme
})
=>
darken
(
0.04
,
theme
.
concreteGray
)}
;
padding: 1rem 1.25rem 1rem 1.25rem;
border-radius: 12px;
`
const
Percent
=
styled
.
div
`
color: inherit;
font-size: 0, 8rem;
flex-grow: 0;
${({
color
,
theme
})
=>
(
color
===
'
faded
'
&&
css
`
color:
${
theme
.
doveGray
}
;
`
)
||
(
color
===
'
red
'
&&
css
`
color:
${
theme
.
salmonRed
}
;
`
)}
;
`
const
Faded
=
styled
.
span
`
opacity: 0.7;
`
const
TransactionInfo
=
styled
.
div
`
padding: 1.25rem 1.25rem 1rem 1.25rem;
`
export
default
function
TransactionDetails
(
props
)
{
const
{
t
}
=
useTranslation
()
const
[
activeIndex
,
setActiveIndex
]
=
useState
(
3
)
const
[
warningType
,
setWarningType
]
=
useState
(
WARNING_TYPE
.
none
)
const
inputRef
=
useRef
()
const
[
showPopup
,
setPopup
]
=
useState
(
false
)
const
[
userInput
,
setUserInput
]
=
useState
(
''
)
const
debouncedInput
=
useDebounce
(
userInput
,
150
)
useEffect
(()
=>
{
if
(
activeIndex
===
4
)
{
checkBounds
(
debouncedInput
)
}
})
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
dropDownContent
=
()
=>
{
return
(
<>
{
renderTransactionDetails
()}
<
SlippageSelector
>
<
SlippageRow
>
Limit
additional
price
slippage
<
QuestionWrapper
onClick
=
{()
=>
{
setPopup
(
!
showPopup
)
}}
onMouseEnter
=
{()
=>
{
setPopup
(
true
)
}}
onMouseLeave
=
{()
=>
{
setPopup
(
false
)
}}
>
<
HelpCircleStyled
src
=
{
question
}
alt
=
"
popup
"
/>
<
/QuestionWrapper
>
{
showPopup
?
(
<
Popup
>
Lowering
this
limit
decreases
your
risk
of
frontrunning
.
However
,
this
makes
it
more
likely
that
your
transaction
will
fail
due
to
normal
price
movements
.
<
/Popup
>
)
:
(
''
)}
<
/SlippageRow
>
<
SlippageRow
wrap
>
<
Option
onClick
=
{()
=>
{
setFromFixed
(
1
,
0.1
)
}}
active
=
{
activeIndex
===
1
}
>
0.1
%
<
/Option
>
<
Option
onClick
=
{()
=>
{
setFromFixed
(
2
,
0.5
)
}}
active
=
{
activeIndex
===
2
}
>
0.5
%
<
/Option
>
<
OptionLarge
onClick
=
{()
=>
{
setFromFixed
(
3
,
1
)
}}
active
=
{
activeIndex
===
3
}
>
1
%
<
Faded
>
(
suggested
)
<
/Faded
>
<
/OptionLarge
>
<
OptionCustom
active
=
{
activeIndex
===
4
}
color
=
{
warningType
===
WARNING_TYPE
.
emptyInput
?
''
:
warningType
!==
WARNING_TYPE
.
none
&&
warningType
!==
WARNING_TYPE
.
riskyEntryLow
?
'
red
'
:
''
}
onClick
=
{()
=>
{
setFromCustom
()
}}
>
<
FlexBetween
>
{
!
(
warningType
===
WARNING_TYPE
.
none
||
warningType
===
WARNING_TYPE
.
emptyInput
)
&&
(
<
span
role
=
"
img
"
aria
-
label
=
"
warning
"
>
⚠️
<
/span
>
)}
<
Input
tabIndex
=
{
-
1
}
ref
=
{
inputRef
}
active
=
{
activeIndex
===
4
}
placeholder
=
{
activeIndex
===
4
?
!!
userInput
?
''
:
'
0
'
:
activeIndex
!==
4
&&
userInput
!==
''
?
userInput
:
'
Custom
'
}
value
=
{
activeIndex
===
4
?
userInput
:
''
}
onChange
=
{
parseInput
}
color
=
{
warningType
===
WARNING_TYPE
.
emptyInput
?
''
:
warningType
!==
WARNING_TYPE
.
none
&&
warningType
!==
WARNING_TYPE
.
riskyEntryLow
?
'
red
'
:
''
}
/
>
<
Percent
color
=
{
activeIndex
!==
4
?
'
faded
'
:
warningType
===
WARNING_TYPE
.
riskyEntryHigh
||
warningType
===
WARNING_TYPE
.
invalidEntryBound
?
'
red
'
:
''
}
>
%
<
/Percent
>
<
/FlexBetween
>
<
/OptionCustom
>
<
/SlippageRow
>
<
SlippageRow
>
<
BottomError
show
=
{
activeIndex
===
4
}
color
=
{
warningType
===
WARNING_TYPE
.
emptyInput
?
''
:
warningType
!==
WARNING_TYPE
.
none
&&
warningType
!==
WARNING_TYPE
.
riskyEntryLow
?
'
red
'
:
''
}
>
{
activeIndex
===
4
&&
warningType
.
toString
()
===
'
none
'
&&
'
Custom slippage value entered
'
}
{
warningType
===
WARNING_TYPE
.
emptyInput
&&
'
Enter a slippage percentage.
'
}
{
warningType
===
WARNING_TYPE
.
invalidEntryBound
&&
'
Please select value less than 50%
'
}
{
warningType
===
WARNING_TYPE
.
riskyEntryHigh
&&
'
Your transaction may be frontrun.
'
}
{
warningType
===
WARNING_TYPE
.
riskyEntryLow
&&
'
Your transaction may fail.
'
}
<
/BottomError
>
<
/SlippageRow
>
<
/SlippageSelector
>
<
/
>
)
}
const
setFromCustom
=
()
=>
{
setActiveIndex
(
4
)
inputRef
.
current
.
focus
()
// if there's a value, evaluate the bounds
checkBounds
(
userInput
)
}
// used for slippage presets
const
setFromFixed
=
(
index
,
slippage
)
=>
{
// update slippage in parent, reset errors and input state
updateSlippage
(
slippage
)
setWarningType
(
WARNING_TYPE
.
none
)
setActiveIndex
(
index
)
props
.
setcustomSlippageError
(
'
valid`
'
)
}
const
checkBounds
=
slippageValue
=>
{
setWarningType
(
WARNING_TYPE
.
none
)
props
.
setcustomSlippageError
(
'
valid
'
)
if
(
slippageValue
===
''
)
{
props
.
setcustomSlippageError
(
'
invalid
'
)
return
setWarningType
(
WARNING_TYPE
.
emptyInput
)
}
// check bounds and set errors
if
(
slippageValue
<
0
||
slippageValue
>
50
)
{
props
.
setcustomSlippageError
(
'
invalid
'
)
return
setWarningType
(
WARNING_TYPE
.
invalidEntryBound
)
}
if
(
slippageValue
>=
0
&&
slippageValue
<
0.1
)
{
props
.
setcustomSlippageError
(
'
valid
'
)
setWarningType
(
WARNING_TYPE
.
riskyEntryLow
)
}
if
(
slippageValue
>
5
)
{
props
.
setcustomSlippageError
(
'
warning
'
)
setWarningType
(
WARNING_TYPE
.
riskyEntryHigh
)
}
//update the actual slippage value in parent
updateSlippage
(
slippageValue
)
}
// check that the theyve entered number and correct decimal
const
parseInput
=
e
=>
{
let
input
=
e
.
target
.
value
// restrict to 2 decimal places
let
acceptableValues
=
[
/^$/
,
/^
\d{1,2}
$/
,
/^
\d{0,2}\.\d{0,2}
$/
]
// if its within accepted decimal limit, update the input state
if
(
acceptableValues
.
some
(
a
=>
a
.
test
(
input
)))
{
setUserInput
(
input
)
}
}
const
updateSlippage
=
newSlippage
=>
{
// round to 2 decimals to prevent ethers error
let
numParsed
=
parseFloat
((
newSlippage
*
100
).
toFixed
(
2
))
// set both slippage values in parents
props
.
setRawSlippage
(
numParsed
)
props
.
setRawTokenSlippage
(
numParsed
)
}
const
renderTransactionDetails
=
()
=>
{
if
(
props
.
independentField
===
props
.
INPUT
)
{
return
(
<
TransactionInfo
>
<
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
>
<
/TransactionInfo
>
)
}
else
{
return
(
<
TransactionInfo
>
<
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
>
<
/TransactionInfo
>
)
}
}
return
<>
{
renderSummary
()}
<
/
>
}
src/pages/Pool/ModeSelector.js
View file @
30887ef1
...
@@ -64,7 +64,7 @@ const StyledNavLink = styled(NavLink).attrs({
...
@@ -64,7 +64,7 @@ const StyledNavLink = styled(NavLink).attrs({
&.
${
activeClassName
}
{
&.
${
activeClassName
}
{
background-color:
${({
theme
})
=>
theme
.
white
}
;
background-color:
${({
theme
})
=>
theme
.
white
}
;
border-radius: 3rem;
border-radius: 3rem;
box-shadow: 0 0
0.5px 0.5
px
${({
theme
})
=>
theme
.
mercuryGray
}
;
box-shadow: 0 0
1px 1
px
${({
theme
})
=>
theme
.
mercuryGray
}
;
font-weight: 500;
font-weight: 500;
color:
${({
theme
})
=>
theme
.
royalBlue
}
;
color:
${({
theme
})
=>
theme
.
royalBlue
}
;
}
}
...
...
src/pages/Send/index.js
View file @
30887ef1
...
@@ -612,7 +612,7 @@ export default function Swap({ initialCurrency }) {
...
@@ -612,7 +612,7 @@ export default function Swap({ initialCurrency }) {
isError
=
{
isError
}
isError
=
{
isError
}
slippageWarning
=
{
slippageWarning
&&
slippageWarningText
}
slippageWarning
=
{
slippageWarning
&&
slippageWarningText
}
highSlippageWarning
=
{
highSlippageWarning
&&
slippageWarningText
}
highSlippageWarning
=
{
highSlippageWarning
&&
slippageWarningText
}
renderTransactionDetails
=
{
renderTransactionDetails
}
dropDownContent
=
{
renderTransactionDetails
}
/
>
/
>
)
)
}
}
...
...
src/pages/Swap/index.js
View file @
30887ef1
...
@@ -7,8 +7,8 @@ import styled from 'styled-components'
...
@@ -7,8 +7,8 @@ import styled from 'styled-components'
import
{
Button
}
from
'
../../theme
'
import
{
Button
}
from
'
../../theme
'
import
CurrencyInputPanel
from
'
../../components/CurrencyInputPanel
'
import
CurrencyInputPanel
from
'
../../components/CurrencyInputPanel
'
import
NewContextualInfo
from
'
../../components/ContextualInfoNew
'
import
OversizedPanel
from
'
../../components/OversizedPanel
'
import
OversizedPanel
from
'
../../components/OversizedPanel
'
import
TransactionDetails
from
'
../../components/TransactionDetails
'
import
ArrowDownBlue
from
'
../../assets/images/arrow-down-blue.svg
'
import
ArrowDownBlue
from
'
../../assets/images/arrow-down-blue.svg
'
import
ArrowDownGrey
from
'
../../assets/images/arrow-down-grey.svg
'
import
ArrowDownGrey
from
'
../../assets/images/arrow-down-grey.svg
'
import
{
amountFormatter
,
calculateGasMargin
}
from
'
../../utils
'
import
{
amountFormatter
,
calculateGasMargin
}
from
'
../../utils
'
...
@@ -26,8 +26,8 @@ const TOKEN_TO_ETH = 1
...
@@ -26,8 +26,8 @@ const TOKEN_TO_ETH = 1
const
TOKEN_TO_TOKEN
=
2
const
TOKEN_TO_TOKEN
=
2
// denominated in bips
// denominated in bips
const
ALLOWED_SLIPPAGE
=
ethers
.
utils
.
bigNumberify
(
200
)
const
ALLOWED_SLIPPAGE
_DEFAULT
=
100
const
TOKEN_ALLOWED_SLIPPAGE
=
ethers
.
utils
.
bigNumberify
(
400
)
const
TOKEN_ALLOWED_SLIPPAGE
_DEFAULT
=
100
// denominated in seconds
// denominated in seconds
const
DEADLINE_FROM_NOW
=
60
*
15
const
DEADLINE_FROM_NOW
=
60
*
15
...
@@ -35,14 +35,6 @@ const DEADLINE_FROM_NOW = 60 * 15
...
@@ -35,14 +35,6 @@ const DEADLINE_FROM_NOW = 60 * 15
// denominated in bips
// denominated in bips
const
GAS_MARGIN
=
ethers
.
utils
.
bigNumberify
(
1000
)
const
GAS_MARGIN
=
ethers
.
utils
.
bigNumberify
(
1000
)
const
BlueSpan
=
styled
.
span
`
color:
${({
theme
})
=>
theme
.
royalBlue
}
;
`
const
LastSummaryText
=
styled
.
div
`
margin-top: 1rem;
`
const
DownArrowBackground
=
styled
.
div
`
const
DownArrowBackground
=
styled
.
div
`
${({
theme
})
=>
theme
.
flexRowNoWrap
}
${({
theme
})
=>
theme
.
flexRowNoWrap
}
justify-content: center;
justify-content: center;
...
@@ -81,9 +73,9 @@ const Flex = styled.div`
...
@@ -81,9 +73,9 @@ const Flex = styled.div`
}
}
`
`
function
calculateSlippageBounds
(
value
,
token
=
false
)
{
function
calculateSlippageBounds
(
value
,
token
=
false
,
tokenAllowedSlippage
,
allowedSlippage
)
{
if
(
value
)
{
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
minimum
=
value
.
sub
(
offset
)
const
maximum
=
value
.
add
(
offset
)
const
maximum
=
value
.
add
(
offset
)
return
{
return
{
...
@@ -244,12 +236,18 @@ export default function Swap({ initialCurrency }) {
...
@@ -244,12 +236,18 @@ export default function Swap({ initialCurrency }) {
const
addTransaction
=
useTransactionAdder
()
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
// analytics
useEffect
(()
=>
{
useEffect
(()
=>
{
ReactGA
.
pageview
(
window
.
location
.
pathname
+
window
.
location
.
search
)
ReactGA
.
pageview
(
window
.
location
.
pathname
+
window
.
location
.
search
)
},
[])
},
[])
// core swap state
// core swap state
-
const
[
swapState
,
dispatchSwapState
]
=
useReducer
(
swapStateReducer
,
initialCurrency
,
getInitialSwapState
)
const
[
swapState
,
dispatchSwapState
]
=
useReducer
(
swapStateReducer
,
initialCurrency
,
getInitialSwapState
)
const
{
independentValue
,
dependentValue
,
independentField
,
inputCurrency
,
outputCurrency
}
=
swapState
const
{
independentValue
,
dependentValue
,
independentField
,
inputCurrency
,
outputCurrency
}
=
swapState
...
@@ -326,7 +324,9 @@ export default function Swap({ initialCurrency }) {
...
@@ -326,7 +324,9 @@ export default function Swap({ initialCurrency }) {
// calculate slippage from target rate
// calculate slippage from target rate
const
{
minimum
:
dependentValueMinumum
,
maximum
:
dependentValueMaximum
}
=
calculateSlippageBounds
(
const
{
minimum
:
dependentValueMinumum
,
maximum
:
dependentValueMaximum
}
=
calculateSlippageBounds
(
dependentValue
,
dependentValue
,
swapType
===
TOKEN_TO_TOKEN
swapType
===
TOKEN_TO_TOKEN
,
tokenAllowedSlippageBig
,
allowedSlippageBig
)
)
// validate input allowance + balance
// validate input allowance + balance
...
@@ -496,118 +496,6 @@ export default function Swap({ initialCurrency }) {
...
@@ -496,118 +496,6 @@ export default function Swap({ initialCurrency }) {
return
`Balance:
${
value
}
`
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
()
{
async
function
onSwap
()
{
const
deadline
=
Math
.
ceil
(
Date
.
now
()
/
1000
)
+
DEADLINE_FROM_NOW
const
deadline
=
Math
.
ceil
(
Date
.
now
()
/
1000
)
+
DEADLINE_FROM_NOW
...
@@ -664,6 +552,8 @@ export default function Swap({ initialCurrency }) {
...
@@ -664,6 +552,8 @@ export default function Swap({ initialCurrency }) {
})
})
}
}
const
[
customSlippageError
,
setcustomSlippageError
]
=
useState
(
''
)
return
(
return
(
<>
<>
<
CurrencyInputPanel
<
CurrencyInputPanel
...
@@ -743,10 +633,39 @@ export default function Swap({ initialCurrency }) {
...
@@ -743,10 +633,39 @@ export default function Swap({ initialCurrency }) {
)}
)}
<
/ExchangeRateWrapper
>
<
/ExchangeRateWrapper
>
<
/OversizedPanel
>
<
/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
}
setcustomSlippageError
=
{
setcustomSlippageError
}
/
>
<
Flex
>
<
Flex
>
<
Button
disabled
=
{
!
isValid
}
onClick
=
{
onSwap
}
warning
=
{
highSlippageWarning
}
>
<
Button
{
highSlippageWarning
?
t
(
'
swapAnyway
'
)
:
t
(
'
swap
'
)}
disabled
=
{
!
isValid
||
customSlippageError
===
'
invalid
'
}
onClick
=
{
onSwap
}
warning
=
{
highSlippageWarning
||
customSlippageError
===
'
warning
'
}
>
{
highSlippageWarning
||
customSlippageError
===
'
warning
'
?
t
(
'
swapAnyway
'
)
:
t
(
'
swap
'
)}
<
/Button
>
<
/Button
>
<
/Flex
>
<
/Flex
>
<
/
>
<
/
>
...
...
src/theme/index.js
View file @
30887ef1
...
@@ -38,6 +38,9 @@ const theme = {
...
@@ -38,6 +38,9 @@ const theme = {
chaliceGray
:
'
#AEAEAE
'
,
chaliceGray
:
'
#AEAEAE
'
,
doveGray
:
'
#737373
'
,
doveGray
:
'
#737373
'
,
mineshaftGray
:
'
#2B2B2B
'
,
mineshaftGray
:
'
#2B2B2B
'
,
buttonOutlineGrey
:
'
#f2f2f2
'
,
//blacks
charcoalBlack
:
'
#404040
'
,
// blues
// blues
zumthorBlue
:
'
#EBF4FF
'
,
zumthorBlue
:
'
#EBF4FF
'
,
malibuBlue
:
'
#5CA2FF
'
,
malibuBlue
:
'
#5CA2FF
'
,
...
@@ -53,6 +56,7 @@ const theme = {
...
@@ -53,6 +56,7 @@ const theme = {
// pink
// pink
uniswapPink
:
'
#DC6BE5
'
,
uniswapPink
:
'
#DC6BE5
'
,
connectedGreen
:
'
#27AE60
'
,
connectedGreen
:
'
#27AE60
'
,
// media queries
// media queries
mediaWidth
:
mediaWidthTemplates
,
mediaWidth
:
mediaWidthTemplates
,
// css snippets
// css snippets
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment