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
4e2c5c1e
Commit
4e2c5c1e
authored
Mar 13, 2020
by
ianlapham
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
slippage without micro warnings on swap
parent
e43d9e03
Changes
20
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
791 additions
and
505 deletions
+791
-505
index.js
src/components/Button/index.js
+38
-2
index.js
src/components/Card/index.js
+2
-2
index.js
src/components/ConfirmationModal/index.js
+20
-237
index.js
src/components/CurrencyInputPanel/index.js
+6
-1
index.tsx
src/components/ExchangePage/index.tsx
+433
-138
index.js
src/components/Header/index.js
+14
-4
index.js
src/components/Menu/index.js
+1
-2
index.tsx
src/components/NumericalInput/index.tsx
+5
-3
index.js
src/components/PoolFinder/index.js
+9
-6
index.js
src/components/PositionCard/index.js
+0
-5
index.js
src/components/Row/index.js
+1
-0
index.js
src/components/SearchModal/index.js
+12
-10
index.ts
src/constants/index.ts
+0
-7
Exchanges.tsx
src/contexts/Exchanges.tsx
+8
-8
App.js
src/pages/App.js
+1
-6
AddLiquidity.tsx
src/pages/Supply/AddLiquidity.tsx
+103
-28
RemoveLiquidity.tsx
src/pages/Supply/RemoveLiquidity.tsx
+110
-30
index.js
src/pages/Supply/index.js
+13
-16
components.js
src/theme/components.js
+1
-0
index.js
src/theme/index.js
+14
-0
No files found.
src/components/Button/index.js
View file @
4e2c5c1e
...
...
@@ -13,6 +13,7 @@ const Base = styled(RebassButton)`
font-weight: 500;
text-align: center;
border-radius: 20px;
border-radius:
${({
borderRadius
})
=>
borderRadius
&&
borderRadius
}
;
outline: none;
border: 1px solid transparent;
color: white;
...
...
@@ -40,8 +41,6 @@ export const ButtonPrimary = styled(Base)`
background-color:
${({
theme
})
=>
theme
.
outlineGrey
}
;
color:
${({
theme
})
=>
theme
.
darkGrey
}
cursor: auto;
outline: none;
border: none;
box-shadow: none;
}
`
...
...
@@ -100,6 +99,27 @@ const ButtonConfirmedStyle = styled(Base)`
}
`
const
ButtonErrorStyle
=
styled
(
Base
)
`
background-color:
${({
theme
})
=>
theme
.
salmonRed
}
;
border: 1px solid
${({
theme
})
=>
theme
.
salmonRed
}
;
&:focus {
box-shadow: 0 0 0 1pt
${({
theme
})
=>
darken
(
0.05
,
theme
.
salmonRed
)}
;
background-color:
${({
theme
})
=>
darken
(
0.05
,
theme
.
salmonRed
)}
;
}
&:hover {
background-color:
${({
theme
})
=>
darken
(
0.05
,
theme
.
salmonRed
)}
;
}
&:active {
box-shadow: 0 0 0 1pt
${({
theme
})
=>
darken
(
0.1
,
theme
.
salmonRed
)}
;
background-color:
${({
theme
})
=>
darken
(
0.1
,
theme
.
salmonRed
)}
;
}
&:disabled {
opacity: 50%;
cursor: auto;
}
`
export
function
ButtonConfirmed
({
children
,
confirmed
,
...
rest
})
{
if
(
confirmed
)
{
return
<
ButtonConfirmedStyle
{...
rest
}
>
{
children
}
<
/ButtonConfirmedStyle
>
...
...
@@ -108,6 +128,14 @@ export function ButtonConfirmed({ children, confirmed, ...rest }) {
}
}
export
function
ButtonError
({
children
,
error
,
...
rest
})
{
if
(
error
)
{
return
<
ButtonErrorStyle
{...
rest
}
>
{
children
}
<
/ButtonErrorStyle
>
}
else
{
return
<
ButtonPrimary
{...
rest
}
>
{
children
}
<
/ButtonPrimary
>
}
}
export
function
ButtonDropwdown
({
disabled
,
children
,
...
rest
})
{
return
(
<
ButtonPrimary
{...
rest
}
>
...
...
@@ -129,3 +157,11 @@ export function ButtonDropwdownLight({ disabled, children, ...rest }) {
<
/ButtonEmpty
>
)
}
export
function
ButtonRadio
({
active
,
children
,
...
rest
})
{
if
(
!
active
)
{
return
<
ButtonEmpty
{...
rest
}
>
{
children
}
<
/ButtonEmpty
>
}
else
{
return
<
ButtonPrimary
{...
rest
}
>
{
children
}
<
/ButtonPrimary
>
}
}
src/components/Card/index.js
View file @
4e2c5c1e
...
...
@@ -3,7 +3,7 @@ import { Box } from 'rebass/styled-components'
const
Card
=
styled
(
Box
)
`
width: 100%;
border-radius:
20
px;
border-radius:
8
px;
padding: 1rem;
padding:
${({
padding
})
=>
padding
}
;
border:
${({
border
})
=>
border
}
;
...
...
@@ -16,5 +16,5 @@ export const LightCard = styled(Card)`
`
export
const
GreyCard
=
styled
(
Card
)
`
background-color: rgba(255, 255, 255, 0.
6
);
background-color: rgba(255, 255, 255, 0.
9
);
`
src/components/ConfirmationModal/index.js
View file @
4e2c5c1e
import
React
from
'
react
'
import
styled
from
'
styled-components
'
import
{
ButtonPrimary
}
from
'
../Button
'
import
{
AutoColumn
,
ColumnCenter
}
from
'
../Column
'
import
Row
,
{
RowBetween
,
RowFlat
,
RowFixed
}
from
'
../Row
'
import
{
ArrowDown
}
from
'
react-feather
'
import
{
ButtonConfirmed
}
from
'
../Button
'
import
{
Text
}
from
'
rebass
'
import
{
LightCard
}
from
'
../Card
'
import
Modal
from
'
../Modal
'
import
{
CheckCircle
}
from
'
react-feather
'
import
DoubleTokenLogo
from
'
../DoubleLogo
'
import
TokenLogo
from
'
../TokenLogo
'
import
{
CloseIcon
}
from
'
../../theme/components
'
import
Loader
from
'
../Loader
'
import
{
Link
}
from
'
../../theme
'
import
{
Text
}
from
'
rebass
'
import
{
CloseIcon
}
from
'
../../theme/components
'
import
{
RowBetween
}
from
'
../Row
'
import
{
CheckCircle
}
from
'
react-feather
'
import
{
ButtonPrimary
}
from
'
../Button
'
import
{
AutoColumn
,
ColumnCenter
}
from
'
../Column
'
import
{
useWeb3React
}
from
'
../../hooks
'
import
{
getEtherscanLink
}
from
'
../../utils
'
import
{
TRANSACTION_TYPE
}
from
'
../../constants
'
const
Wrapper
=
styled
.
div
`
width: 100%;
`
const
Section
=
styled
(
AutoColumn
)
`
padding: 2
rem
;
padding: 2
6px
;
`
const
BottomSection
=
styled
(
Section
)
`
...
...
@@ -36,29 +29,17 @@ const ConfirmedIcon = styled(ColumnCenter)`
padding: 60px 0;
`
const
ConfirmedText
=
styled
(
Text
)
`
color:
${({
theme
,
confirmed
})
=>
(
confirmed
?
theme
.
connectedGreen
:
theme
.
white
)}
;
`
export
default
function
ConfirmationModal
({
isOpen
,
onDismiss
,
liquidityAmount
=
undefined
,
poolTokenPercentage
=
undefined
,
amount0
,
amount1
,
price
,
transactionType
,
hash
,
signed
=
false
,
contractCall
,
attempt
edRemoval
=
false
,
topContent
,
bottomContent
,
attempt
ingTxn
,
pendingConfirmation
,
extraCall
=
undefined
pendingText
,
title
=
''
})
{
const
{
address
:
address0
,
symbol
:
symbol0
}
=
amount0
?.
token
||
{}
const
{
address
:
address1
,
symbol
:
symbol1
}
=
amount1
?.
token
||
{}
const
{
chainId
}
=
useWeb3React
()
function
WrappedOnDismissed
()
{
...
...
@@ -66,208 +47,19 @@ export default function ConfirmationModal({
}
return
(
<
Modal
isOpen
=
{
isOpen
}
onDismiss
=
{
WrappedOnDismissed
}
>
{
!
attempt
edRemoval
?
(
<
Modal
isOpen
=
{
isOpen
}
onDismiss
=
{
WrappedOnDismissed
}
maxHeight
=
{
90
}
>
{
!
attempt
ingTxn
?
(
<
Wrapper
>
<
Section
gap
=
"
40px
"
>
<
Section
>
<
RowBetween
>
<
Text
fontWeight
=
{
500
}
fontSize
=
{
'
20px
'
}
>
{
t
ransactionType
===
TRANSACTION_TYPE
.
SWAP
?
'
Confirm Swap
'
:
'
You will receive
'
}
<
Text
fontWeight
=
{
500
}
fontSize
=
{
20
}
>
{
t
itle
}
<
/Text
>
<
CloseIcon
onClick
=
{
WrappedOnDismissed
}
/
>
<
/RowBetween
>
{
transactionType
===
TRANSACTION_TYPE
.
SWAP
&&
(
<
AutoColumn
gap
=
{
'
20px
'
}
>
<
LightCard
>
<
RowBetween
>
<
Text
fontSize
=
{
24
}
fontWeight
=
{
500
}
>
{
!!
amount0
&&
amount0
?.
toSignificant
(
6
)}
<
/Text
>
<
RowFixed
gap
=
"
10px
"
>
<
TokenLogo
address
=
{
amount0
?.
token
?.
address
}
size
=
{
'
24px
'
}
/
>
<
Text
fontSize
=
{
24
}
fontWeight
=
{
500
}
style
=
{{
marginLeft
:
'
10px
'
}}
>
{
symbol0
}
<
/Text
>
<
/RowFixed
>
<
/RowBetween
>
<
/LightCard
>
<
ColumnCenter
>
<
ArrowDown
size
=
"
16
"
color
=
"
#888D9B
"
/>
<
/ColumnCenter
>
<
LightCard
>
<
RowBetween
>
<
Text
fontSize
=
{
24
}
fontWeight
=
{
500
}
>
{
!!
amount1
&&
amount1
?.
toSignificant
(
6
)}
<
/Text
>
<
RowFixed
gap
=
"
10px
"
>
<
TokenLogo
address
=
{
amount1
?.
token
?.
address
}
size
=
{
'
24px
'
}
/
>
<
Text
fontSize
=
{
24
}
fontWeight
=
{
500
}
style
=
{{
marginLeft
:
'
10px
'
}}
>
{
symbol1
}
<
/Text
>
<
/RowFixed
>
<
/RowBetween
>
<
/LightCard
>
<
/AutoColumn
>
)}
{
transactionType
===
TRANSACTION_TYPE
.
ADD
&&
(
<
AutoColumn
gap
=
"
16px
"
>
<
RowFlat
>
<
Text
fontSize
=
"
48px
"
fontWeight
=
{
500
}
lineHeight
=
"
32px
"
marginRight
=
{
10
}
>
{
liquidityAmount
?.
toFixed
(
6
)}
<
/Text
>
<
DoubleTokenLogo
a0
=
{
address0
||
''
}
a1
=
{
address1
||
''
}
size
=
{
20
}
/
>
<
/RowFlat
>
<
Row
>
<
Text
fontSize
=
"
24px
"
>
{
symbol0
+
'
:
'
+
symbol1
+
'
Pool Tokens
'
}
<
/Text
>
<
/Row
>
<
/AutoColumn
>
)}
{
transactionType
===
TRANSACTION_TYPE
.
REMOVE
&&
(
<
AutoColumn
gap
=
"
16px
"
>
<
Row
>
<
TokenLogo
address
=
{
address0
}
size
=
{
'
30px
'
}
/
>
<
Text
fontSize
=
"
24px
"
marginLeft
=
{
10
}
>
{
symbol0
}
{
!!
amount0
&&
amount0
?.
toSignificant
(
8
)}
<
/Text
>
<
/Row
>
<
Row
>
<
TokenLogo
address
=
{
address1
}
size
=
{
'
30px
'
}
/
>
<
Text
fontSize
=
"
24px
"
marginLeft
=
{
10
}
>
{
symbol1
}
{
!!
amount1
&&
amount1
?.
toSignificant
(
8
)}
<
/Text
>
<
/Row
>
<
/AutoColumn
>
)}
{
topContent
()}
<
/Section
>
<
BottomSection
gap
=
"
12px
"
>
<
AutoColumn
gap
=
"
12px
"
>
{
transactionType
===
TRANSACTION_TYPE
.
ADD
&&
(
<>
<
RowBetween
>
<
Text
color
=
"
#565A69
"
fontWeight
=
{
500
}
fontSize
=
{
16
}
>
{
symbol0
}
Deposited
<
/Text
>
<
RowFixed
>
<
TokenLogo
address
=
{
address0
||
''
}
style
=
{{
marginRight
:
'
8px
'
}}
/
>
<
Text
fontWeight
=
{
500
}
fontSize
=
{
16
}
>
{
!!
amount0
&&
amount0
?.
toSignificant
(
6
)}
<
/Text
>
<
/RowFixed
>
<
/RowBetween
>
<
RowBetween
>
<
Text
color
=
"
#565A69
"
fontWeight
=
{
500
}
fontSize
=
{
16
}
>
{
symbol1
}
Deposited
<
/Text
>
<
RowFixed
>
<
TokenLogo
address
=
{
address1
||
''
}
style
=
{{
marginRight
:
'
8px
'
}}
/
>
<
Text
fontWeight
=
{
500
}
fontSize
=
{
16
}
>
{
amount1
?.
toSignificant
(
6
)}
<
/Text
>
<
/RowFixed
>
<
/RowBetween
>
<
/
>
)}
{
transactionType
===
TRANSACTION_TYPE
.
REMOVE
&&
(
<
RowBetween
>
<
Text
color
=
"
#565A69
"
fontWeight
=
{
500
}
fontSize
=
{
16
}
>
{
'
UNI
'
+
symbol0
+
'
:
'
+
symbol1
}
Burned
<
/Text
>
<
RowFixed
>
<
DoubleTokenLogo
a0
=
{
address0
||
''
}
a1
=
{
address1
||
''
}
margin
=
{
true
}
/
>
<
Text
fontWeight
=
{
500
}
fontSize
=
{
16
}
>
{
liquidityAmount
?.
toSignificant
(
6
)}
<
/Text
>
<
/RowFixed
>
<
/RowBetween
>
)}
{
price
&&
price
?.
adjusted
&&
(
<
RowBetween
>
<
Text
color
=
"
#565A69
"
fontWeight
=
{
500
}
fontSize
=
{
16
}
>
Rate
<
/Text
>
<
Text
fontWeight
=
{
500
}
fontSize
=
{
16
}
>
{
`1
${
symbol0
}
=
${
price
.
adjusted
.
toFixed
(
8
)}
${
symbol1
}
`
}
<
/Text
>
<
/RowBetween
>
)}
{
transactionType
===
TRANSACTION_TYPE
.
ADD
&&
poolTokenPercentage
&&
(
<
RowBetween
>
<
Text
color
=
"
#565A69
"
fontWeight
=
{
500
}
fontSize
=
{
16
}
>
Minted
Pool
Share
:
<
/Text
>
<
Text
fontWeight
=
{
500
}
fontSize
=
{
16
}
>
{
poolTokenPercentage
?.
toFixed
(
6
)
+
'
%
'
}
<
/Text
>
<
/RowBetween
>
)}
{
transactionType
===
TRANSACTION_TYPE
.
REMOVE
?
(
<
RowBetween
gap
=
"
20px
"
>
<
ButtonConfirmed
style
=
{{
margin
:
'
20px 0
'
}}
width
=
"
48%
"
onClick
=
{()
=>
{
extraCall
()
}}
confirmed
=
{
signed
}
disabled
=
{
signed
}
>
<
ConfirmedText
fontWeight
=
{
500
}
fontSize
=
{
20
}
confirmed
=
{
signed
}
>
{
signed
?
'
Signed
'
:
'
Sign
'
}
<
/ConfirmedText
>
<
/ButtonConfirmed
>
<
ButtonPrimary
width
=
"
48%
"
disabled
=
{
!
signed
}
style
=
{{
margin
:
'
20px 0
'
}}
onClick
=
{()
=>
{
contractCall
()
}}
>
<
Text
fontWeight
=
{
500
}
fontSize
=
{
20
}
>
Confirm
Remove
<
/Text
>
<
/ButtonPrimary
>
<
/RowBetween
>
)
:
(
<
ButtonPrimary
style
=
{{
margin
:
'
20px 0
'
}}
onClick
=
{()
=>
{
contractCall
()
}}
>
<
Text
fontWeight
=
{
500
}
fontSize
=
{
20
}
>
Confirm
{
'
'
}
{
transactionType
===
TRANSACTION_TYPE
.
ADD
?
'
Supply
'
:
transactionType
===
TRANSACTION_TYPE
.
REMOVE
?
'
Remove
'
:
'
Swap
'
}
<
/Text
>
<
/ButtonPrimary
>
)}
{
transactionType
===
TRANSACTION_TYPE
.
ADD
&&
(
<
Text
fontSize
=
{
12
}
color
=
"
#565A69
"
textAlign
=
"
center
"
>
{
`Output is estimated. You will receive at least
${
liquidityAmount
?.
toFixed
(
6
)}
UNI
$
{
symbol0
}
/${symbol1} or the transaction will revert.`
}
<
/Text
>
)}
{
transactionType
===
TRANSACTION_TYPE
.
REMOVE
&&
(
<
Text
fontSize
=
{
12
}
color
=
"
#565A69
"
textAlign
=
"
center
"
>
{
`Output is estimated. You will receive at least
${
amount0
?.
toSignificant
(
6
)}
$
{
symbol0
}
at
least
$
{
amount1
?.
toSignificant
(
6
)}
$
{
symbol1
}
or
the
transaction
will
revert
.
`}
</Text>
)}
{transactionType === TRANSACTION_TYPE.SWAP && (
<Text fontSize={12} color="#565A69" textAlign="center">
{`
Output
is
estimated
.
You
will
receive
at
least
$
{
amount1
?.
toSignificant
(
6
)}
$
{
symbol1
}
or
the
transaction
will
revert
.
`}
</Text>
)}
</AutoColumn>
</BottomSection>
<
BottomSection
gap
=
"
12px
"
>
{
bottomContent
()}
<
/BottomSection
>
<
/Wrapper
>
)
:
(
<
Wrapper
>
...
...
@@ -284,17 +76,8 @@ export default function ConfirmationModal({
{
!
pendingConfirmation
?
'
Transaction Submitted
'
:
'
Waiting For Confirmation
'
}
<
/Text
>
<
AutoColumn
gap
=
"
12px
"
justify
=
{
'
center
'
}
>
<Text fontWeight={500} fontSize={16} color="#2172E5">
{transactionType === TRANSACTION_TYPE.ADD
? 'Supplied'
: transactionType === TRANSACTION_TYPE.REMOVE
? 'Removed'
: 'Swapped'}
</Text>
<
Text
fontWeight
=
{
600
}
fontSize
=
{
16
}
color
=
"
#2172E5
"
>
{`
$
{
amount0
?.
toSignificant
(
6
)}
$
{
symbol0
}
$
{
transactionType
===
TRANSACTION_TYPE
.
SWAP
?
'
for
'
:
'
and
'
}
$
{
amount1
?.
toSignificant
(
6
)}
$
{
symbol1
}
`}
{
pendingText
}
<
/Text
>
<
/AutoColumn
>
{
!
pendingConfirmation
&&
(
...
...
src/components/CurrencyInputPanel/index.js
View file @
4e2c5c1e
...
...
@@ -251,7 +251,12 @@ export default function CurrencyInputPanel({
<
/LabelRow
>
)}
<
InputRow
>
<
NumericalInput
field
=
{
field
}
value
=
{
value
}
onUserInput
=
{
onUserInput
}
/
>
<
NumericalInput
value
=
{
value
}
onUserInput
=
{
val
=>
{
onUserInput
(
field
,
val
)
}}
/
>
{
!!
token
?.
address
&&
!
atMax
&&
<
StyledBalanceMax
onClick
=
{
onMax
}
>
MAX
<
/StyledBalanceMax>
}
{
renderUnlockButton
()}
<
CurrencySelect
...
...
src/components/ExchangePage/index.tsx
View file @
4e2c5c1e
...
...
@@ -4,6 +4,21 @@ import { ethers } from 'ethers'
import
{
parseUnits
,
parseEther
}
from
'
@ethersproject/units
'
import
{
WETH
,
TradeType
,
Route
,
Trade
,
TokenAmount
,
JSBI
}
from
'
@uniswap/sdk
'
import
TokenLogo
from
'
../TokenLogo
'
import
QuestionHelper
from
'
../Question
'
import
NumericalInput
from
'
../NumericalInput
'
import
ConfirmationModal
from
'
../ConfirmationModal
'
import
CurrencyInputPanel
from
'
../CurrencyInputPanel
'
import
{
Link
}
from
'
../../theme/components
'
import
{
Text
}
from
'
rebass
'
import
ThemeProvider
,
{
TYPE
}
from
'
../../theme
'
import
{
GreyCard
}
from
'
../../components/Card
'
import
{
ArrowDown
,
ArrowUp
}
from
'
react-feather
'
import
{
AutoColumn
,
ColumnCenter
}
from
'
../../components/Column
'
import
{
ButtonError
,
ButtonRadio
}
from
'
../Button
'
import
Row
,
{
RowBetween
,
RowFixed
}
from
'
../../components/Row
'
import
{
usePopups
}
from
'
../../contexts/Application
'
import
{
useToken
}
from
'
../../contexts/Tokens
'
import
{
useExchange
}
from
'
../../contexts/Exchanges
'
import
{
useWeb3React
}
from
'
../../hooks
'
...
...
@@ -11,17 +26,13 @@ import { useAddressBalance } from '../../contexts/Balances'
import
{
useTransactionAdder
}
from
'
../../contexts/Transactions
'
import
{
useAddressAllowance
}
from
'
../../contexts/Allowances
'
import
ConfirmationModal
from
'
../ConfirmationModal
'
import
CurrencyInputPanel
from
'
../CurrencyInputPanel
'
import
{
Text
}
from
'
rebass
'
import
{
RowBetween
}
from
'
../../components/Row
'
import
{
ButtonPrimary
}
from
'
../Button
'
import
{
ArrowDown
,
ArrowUp
}
from
'
react-feather
'
import
{
AutoColumn
,
ColumnCenter
}
from
'
../../components/Column
'
import
{
TRANSACTION_TYPE
,
ROUTER_ADDRESSES
}
from
'
../../constants
'
import
{
ROUTER_ADDRESSES
}
from
'
../../constants
'
import
{
getRouterContract
,
calculateGasMargin
}
from
'
../../utils
'
const
Wrapper
=
styled
.
div
`
position: relative;
`
const
ArrowWrapper
=
styled
.
div
`
padding: 4px;
border: 1px solid
${({
theme
})
=>
theme
.
malibuBlue
}
;
...
...
@@ -36,8 +47,24 @@ const ArrowWrapper = styled.div`
}
`
const
FixedBottom
=
styled
.
div
`
position: absolute;
bottom: -200px;
width: 100%;
`
const
ErrorText
=
styled
(
Text
)
`
color:
${({
theme
,
error
})
=>
(
error
?
theme
.
salmonRed
:
theme
.
chaliceGray
)}
;
color:
${({
theme
,
warningMedium
,
warningHigh
})
=>
warningHigh
?
theme
.
salmonRed
:
warningMedium
?
theme
.
warningYellow
:
theme
.
textColor
}
;
`
const
InputWrapper
=
styled
(
RowBetween
)
`
width: 200px;
background-color:
${({
theme
})
=>
theme
.
inputBackground
}
;
border-radius: 8px;
padding: 4px 8px;
border: 1px solid transparent;
border:
${({
active
,
theme
})
=>
active
&&
'
1px solid
'
+
theme
.
royalBlue
}
;
`
enum
Field
{
...
...
@@ -136,25 +163,74 @@ function reducer(
}
}
function
hex
(
value
:
JSBI
)
{
return
ethers
.
utils
.
bigNumberify
(
value
.
toString
())
}
const
SLIPPAGE_INDEX
=
{
1
:
1
,
2
:
2
,
3
:
3
,
4
:
4
}
const
SWAP_TYPE
=
{
EXACT_TOKENS_FOR_TOKENS
:
'
EXACT_TOKENS_FOR_TOKENS
'
,
EXACT_TOKENS_FOR_ETH
:
'
EXACT_TOKENS_FOR_ETH
'
,
EXACT_ETH_FOR_TOKENS
:
'
EXACT_ETH_FOR_TOKENS
'
,
TOKENS_FOR_EXACT_TOKENS
:
'
TOKENS_FOR_EXACT_TOKENS
'
,
TOKENS_FOR_EXACT_ETH
:
'
TOKENS_FOR_EXACT_ETH
'
,
ETH_FOR_EXACT_TOKENS
:
'
ETH_FOR_EXACT_TOKENS
'
}
const
GAS_MARGIN
=
ethers
.
utils
.
bigNumberify
(
1000
)
// default allowed slippage, in bips
const
INITIAL_ALLOWED_SLIPPAGE
=
200
// 15 minutes, denominated in seconds
const
DEFAULT_DEADLINE_FROM_NOW
=
60
*
15
// used for warning states based on slippage in bips
const
ALLOWED_IMPACT_MEDIUM
=
100
const
ALLOWED_IMPACT_HIGH
=
500
export
default
function
ExchangePage
()
{
const
{
chainId
,
account
,
library
}
=
useWeb3React
()
const
routerAddress
=
ROUTER_ADDRESSES
[
chainId
]
// adding notifications on txns
const
[,
addPopup
]
=
usePopups
()
// input details
const
[
state
,
dispatch
]
=
useReducer
(
reducer
,
WETH
[
chainId
].
address
,
initializeSwapState
)
const
{
independentField
,
typedValue
,
...
fieldData
}
=
state
// get derived state
const
dependentField
=
independentField
===
Field
.
INPUT
?
Field
.
OUTPUT
:
Field
.
INPUT
const
tradeType
=
independentField
===
Field
.
INPUT
?
TradeType
.
EXACT_INPUT
:
TradeType
.
EXACT_OUTPUT
const
[
tradeError
,
setTradeError
]
=
useState
(
''
)
// error for thinsg liek reserve sizes
// get basic SDK entities
const
tokens
=
{
[
Field
.
INPUT
]:
useToken
(
fieldData
[
Field
.
INPUT
].
address
),
[
Field
.
OUTPUT
]:
useToken
(
fieldData
[
Field
.
OUTPUT
].
address
)
}
const
exchange
=
useExchange
(
tokens
[
Field
.
INPUT
],
tokens
[
Field
.
OUTPUT
])
const
route
=
!!
exchange
?
new
Route
([
exchange
],
tokens
[
Field
.
INPUT
])
:
undefined
// no useRoute hook
const
route
=
!!
exchange
?
new
Route
([
exchange
],
tokens
[
Field
.
INPUT
])
:
undefined
// modal state
const
addTransaction
=
useTransactionAdder
()
const
[
showConfirm
,
setShowConfirm
]
=
useState
(
true
)
const
[
pendingConfirmation
,
setPendingConfirmation
]
=
useState
(
true
)
// waiting for user confirmation
const
[
attemptingTxn
,
setAttemptingTxn
]
=
useState
(
false
)
// clicked confirmed
// txn values
const
[
txHash
,
setTxHash
]
=
useState
()
const
[
deadline
,
setDeadline
]
=
useState
(
DEFAULT_DEADLINE_FROM_NOW
)
const
[
allowedSlippage
,
setAllowedSlippage
]
=
useState
(
INITIAL_ALLOWED_SLIPPAGE
)
// approvals
const
inputApproval
=
useAddressAllowance
(
account
,
tokens
[
Field
.
INPUT
],
routerAddress
)
const
outputApproval
=
useAddressAllowance
(
account
,
tokens
[
Field
.
OUTPUT
],
routerAddress
)
// get user- and token-specific lookup data
const
userBalances
=
{
...
...
@@ -164,14 +240,18 @@ export default function ExchangePage() {
const
parsedAmounts
:
{
[
field
:
number
]:
TokenAmount
}
=
{}
// try to parse typed value
if
(
typedValue
!==
''
&&
typedValue
!==
'
.
'
&&
tokens
[
independentField
])
{
// if (typedValue !== '' && typedValue !== '.' && tokens[independentField]) {
if
(
tokens
[
independentField
])
{
try
{
const
typedValueParsed
=
parseUnits
(
typedValue
,
tokens
[
independentField
].
decimals
).
toString
()
const
typedValueParsed
=
parseUnits
(
'
0.0001
'
,
tokens
[
independentField
].
decimals
).
toString
()
if
(
typedValueParsed
!==
'
0
'
)
parsedAmounts
[
independentField
]
=
new
TokenAmount
(
tokens
[
independentField
],
typedValueParsed
)
}
catch
(
error
)
{
// should only fail if the user specifies too many decimal places of precision (or maybe exceed max uint?)
console
.
error
(
error
)
/**
* @todo reserve limit error here
*/
console
.
error
(
'
found error here
'
)
}
}
...
...
@@ -182,9 +262,9 @@ export default function ExchangePage() {
!!
route
&&
!!
parsedAmounts
[
independentField
]
?
new
Trade
(
route
,
parsedAmounts
[
independentField
],
tradeType
)
:
undefined
}
catch
(
error
)
{
console
.
error
(
error
)
}
}
catch
(
error
)
{
}
const
slippageFromTrade
=
trade
&&
trade
.
slippage
if
(
trade
)
parsedAmounts
[
dependentField
]
=
tradeType
===
TradeType
.
EXACT_INPUT
?
trade
.
outputAmount
:
trade
.
inputAmount
...
...
@@ -250,14 +330,8 @@ export default function ExchangePage() {
:
undefined
const
maxAmountOutput
=
!!
userBalances
[
Field
.
OUTPUT
]
&&
JSBI
.
greaterThan
(
userBalances
[
Field
.
OUTPUT
].
raw
,
tokens
[
Field
.
OUTPUT
].
equals
(
WETH
[
chainId
])
?
MIN_ETHER
.
raw
:
JSBI
.
BigInt
(
0
)
)
?
tokens
[
Field
.
OUTPUT
].
equals
(
WETH
[
chainId
])
?
userBalances
[
Field
.
OUTPUT
].
subtract
(
MIN_ETHER
)
:
userBalances
[
Field
.
OUTPUT
]
!!
userBalances
[
Field
.
OUTPUT
]
&&
JSBI
.
greaterThan
(
userBalances
[
Field
.
OUTPUT
].
raw
,
JSBI
.
BigInt
(
0
))
?
userBalances
[
Field
.
OUTPUT
]
:
undefined
const
atMaxAmountOutput
=
...
...
@@ -265,23 +339,6 @@ export default function ExchangePage() {
?
JSBI
.
equal
(
maxAmountOutput
.
raw
,
parsedAmounts
[
Field
.
OUTPUT
].
raw
)
:
undefined
const
[
showConfirm
,
setShowConfirm
]
=
useState
(
false
)
const
[
pendingConfirmation
,
toggelPendingConfirmation
]
=
useState
(
true
)
// state for txn
const
addTransaction
=
useTransactionAdder
()
const
[
txHash
,
setTxHash
]
=
useState
()
const
SWAP_TYPE
=
{
EXACT_TOKENS_FOR_TOKENS
:
'
EXACT_TOKENS_FOR_TOKENS
'
,
EXACT_TOKENS_FOR_ETH
:
'
EXACT_TOKENS_FOR_ETH
'
,
EXACT_ETH_FOR_TOKENS
:
'
EXACT_ETH_FOR_TOKENS
'
,
TOKENS_FOR_EXACT_TOKENS
:
'
TOKENS_FOR_EXACT_TOKENS
'
,
TOKENS_FOR_EXACT_ETH
:
'
TOKENS_FOR_EXACT_ETH
'
,
ETH_FOR_EXACT_TOKENS
:
'
ETH_FOR_EXACT_TOKENS
'
}
function
getSwapType
()
{
if
(
tradeType
===
TradeType
.
EXACT_INPUT
)
{
if
(
tokens
[
Field
.
INPUT
]
===
WETH
[
chainId
])
{
...
...
@@ -302,51 +359,62 @@ export default function ExchangePage() {
}
}
const
ALLOWED_SLIPPAGE
=
100
function
calculateSlippageAmount
(
value
:
TokenAmount
):
JSBI
[]
{
if
(
value
&&
value
.
raw
)
{
const
offset
=
JSBI
.
divide
(
JSBI
.
multiply
(
JSBI
.
BigInt
(
ALLOWED_SLIPPAGE
),
value
.
raw
),
JSBI
.
BigInt
(
10000
))
const
offset
=
JSBI
.
divide
(
JSBI
.
multiply
(
JSBI
.
BigInt
(
allowedSlippage
),
value
.
raw
),
JSBI
.
BigInt
(
10000
))
return
[
JSBI
.
subtract
(
value
.
raw
,
offset
),
JSBI
.
add
(
value
.
raw
,
offset
)]
}
return
null
}
function
hex
(
value
:
JSBI
)
{
return
ethers
.
utils
.
bigNumberify
(
value
.
toString
())
}
const
slippageAdjustedAmountsRaw
=
{
const
slippageAdjustedAmounts
=
{
[
Field
.
INPUT
]:
Field
.
INPUT
===
independentField
?
parsedAmounts
[
Field
.
INPUT
]?.
raw
:
calculateSlippageAmount
(
parsedAmounts
[
Field
.
INPUT
])?.[
1
],
?
parsedAmounts
[
Field
.
INPUT
]
:
calculateSlippageAmount
(
parsedAmounts
[
Field
.
INPUT
])?.[
0
]
&&
new
TokenAmount
(
tokens
[
Field
.
INPUT
],
calculateSlippageAmount
(
parsedAmounts
[
Field
.
INPUT
])?.[
1
]),
[
Field
.
OUTPUT
]:
Field
.
OUTPUT
===
independentField
?
parsedAmounts
[
Field
.
OUTPUT
]?.
raw
:
calculateSlippageAmount
(
parsedAmounts
[
Field
.
OUTPUT
])?.[
0
]
?
parsedAmounts
[
Field
.
OUTPUT
]
:
calculateSlippageAmount
(
parsedAmounts
[
Field
.
OUTPUT
])?.[
0
]
&&
new
TokenAmount
(
tokens
[
Field
.
INPUT
],
calculateSlippageAmount
(
parsedAmounts
[
Field
.
OUTPUT
])?.[
0
])
}
const
inputApproval
=
useAddressAllowance
(
account
,
tokens
[
Field
.
INPUT
],
routerAddress
)
const
outputApproval
=
useAddressAllowance
(
account
,
tokens
[
Field
.
OUTPUT
],
routerAddress
)
const
showInputUnlock
=
parsedAmounts
[
Field
.
INPUT
]
&&
inputApproval
&&
JSBI
.
greaterThan
(
parsedAmounts
[
Field
.
INPUT
].
raw
,
inputApproval
.
raw
)
const
[
showInputUnlock
,
setShowInputUnlock
]
=
useState
(
false
)
const
showOutputUnlock
=
parsedAmounts
[
Field
.
OUTPUT
]
&&
outputApproval
&&
JSBI
.
greaterThan
(
parsedAmounts
[
Field
.
OUTPUT
].
raw
,
outputApproval
.
raw
)
// monitor parsed amounts and update unlocked buttons
useEffect
(()
=>
{
if
(
parsedAmounts
[
Field
.
INPUT
]
&&
inputApproval
&&
JSBI
.
greaterThan
(
parsedAmounts
[
Field
.
INPUT
].
raw
,
inputApproval
.
raw
)
)
{
setShowInputUnlock
(
true
)
}
else
{
setShowInputUnlock
(
false
)
// modal state
const
[
showAdvanced
,
setShowAdvanced
]
=
useState
(
true
)
const
[
activeIndex
,
setActiveIndex
]
=
useState
(
SLIPPAGE_INDEX
[
3
])
const
[
customSlippage
,
setCustomSlippage
]
=
useState
()
const
[
customDeadline
,
setCustomDeadline
]
=
useState
(
DEFAULT_DEADLINE_FROM_NOW
/
60
)
const
[
slippageInputError
,
setSlippageInputError
]
=
useState
(
null
)
function
parseCustomInput
(
val
)
{
const
acceptableValues
=
[
/^$/
,
/^
\d{1,2}
$/
,
/^
\d{0,2}\.\d{0,2}
$/
]
if
(
acceptableValues
.
some
(
a
=>
a
.
test
(
val
)))
{
setCustomSlippage
(
val
)
setAllowedSlippage
(
val
*
100
)
}
}
function
parseCustomDeadline
(
val
)
{
const
acceptableValues
=
[
/^$/
,
/^
\d
+$/
]
if
(
acceptableValues
.
some
(
re
=>
re
.
test
(
val
)))
{
setCustomDeadline
(
val
)
setDeadline
(
val
*
60
)
}
}
,
[
inputApproval
,
outputApproval
,
parsedAmounts
])
}
async
function
onSwap
()
{
const
routerContract
=
getRouterContract
(
chainId
,
library
,
account
)
setAttemptingTxn
(
true
)
const
path
=
Object
.
keys
(
route
.
path
).
map
(
key
=>
{
return
route
.
path
[
key
].
address
...
...
@@ -354,7 +422,7 @@ export default function ExchangePage() {
let
estimate
:
Function
,
method
:
Function
,
args
,
value
const
deadline
=
1739591241
const
deadline
FromNow
=
Math
.
ceil
(
Date
.
now
()
/
1000
)
+
deadline
const
swapType
=
getSwapType
()
switch
(
swapType
)
{
...
...
@@ -362,11 +430,11 @@ export default function ExchangePage() {
estimate
=
routerContract
.
estimate
.
swapExactTokensForTokens
method
=
routerContract
.
swapExactTokensForTokens
args
=
[
slippageAdjustedAmounts
Raw
[
Field
.
INPUT
]
.
toString
(),
slippageAdjustedAmounts
Raw
[
Field
.
OUTPUT
]
.
toString
(),
slippageAdjustedAmounts
[
Field
.
INPUT
].
raw
.
toString
(),
slippageAdjustedAmounts
[
Field
.
OUTPUT
].
raw
.
toString
(),
path
,
account
,
deadline
deadline
FromNow
]
value
=
ethers
.
constants
.
Zero
break
...
...
@@ -374,29 +442,29 @@ export default function ExchangePage() {
estimate
=
routerContract
.
estimate
.
swapTokensForExactTokens
method
=
routerContract
.
swapTokensForExactTokens
args
=
[
slippageAdjustedAmounts
Raw
[
Field
.
OUTPUT
]
.
toString
(),
slippageAdjustedAmounts
Raw
[
Field
.
INPUT
]
.
toString
(),
slippageAdjustedAmounts
[
Field
.
OUTPUT
].
raw
.
toString
(),
slippageAdjustedAmounts
[
Field
.
INPUT
].
raw
.
toString
(),
path
,
account
,
deadline
deadline
FromNow
]
value
=
ethers
.
constants
.
Zero
break
case
SWAP_TYPE
.
EXACT_ETH_FOR_TOKENS
:
estimate
=
routerContract
.
estimate
.
swapExactETHForTokens
method
=
routerContract
.
swapExactETHForTokens
args
=
[
slippageAdjustedAmounts
Raw
[
Field
.
OUTPUT
].
toString
(),
path
,
account
,
deadline
]
value
=
hex
(
slippageAdjustedAmounts
Raw
[
Field
.
INPUT
]
)
args
=
[
slippageAdjustedAmounts
[
Field
.
OUTPUT
].
raw
.
toString
(),
path
,
account
,
deadlineFromNow
]
value
=
hex
(
slippageAdjustedAmounts
[
Field
.
INPUT
].
raw
)
break
case
SWAP_TYPE
.
TOKENS_FOR_EXACT_ETH
:
estimate
=
routerContract
.
estimate
.
swapTokensForExactETH
method
=
routerContract
.
swapTokensForExactETH
args
=
[
slippageAdjustedAmounts
Raw
[
Field
.
OUTPUT
]
.
toString
(),
slippageAdjustedAmounts
Raw
[
Field
.
INPUT
]
.
toString
(),
slippageAdjustedAmounts
[
Field
.
OUTPUT
].
raw
.
toString
(),
slippageAdjustedAmounts
[
Field
.
INPUT
].
raw
.
toString
(),
path
,
account
,
deadline
deadline
FromNow
]
value
=
ethers
.
constants
.
Zero
break
...
...
@@ -404,24 +472,22 @@ export default function ExchangePage() {
estimate
=
routerContract
.
estimate
.
swapExactTokensForETH
method
=
routerContract
.
swapExactTokensForETH
args
=
[
slippageAdjustedAmounts
Raw
[
Field
.
INPUT
]
.
toString
(),
slippageAdjustedAmounts
Raw
[
Field
.
OUTPUT
]
.
toString
(),
slippageAdjustedAmounts
[
Field
.
INPUT
].
raw
.
toString
(),
slippageAdjustedAmounts
[
Field
.
OUTPUT
].
raw
.
toString
(),
path
,
account
,
deadline
deadline
FromNow
]
value
=
ethers
.
constants
.
Zero
break
case
SWAP_TYPE
.
ETH_FOR_EXACT_TOKENS
:
estimate
=
routerContract
.
estimate
.
swapETHForExactTokens
method
=
routerContract
.
swapETHForExactTokens
args
=
[
slippageAdjustedAmounts
Raw
[
Field
.
OUTPUT
].
toString
(),
path
,
account
,
deadline
]
value
=
hex
(
slippageAdjustedAmounts
Raw
[
Field
.
INPUT
]
)
args
=
[
slippageAdjustedAmounts
[
Field
.
OUTPUT
].
raw
.
toString
(),
path
,
account
,
deadlineFromNow
]
value
=
hex
(
slippageAdjustedAmounts
[
Field
.
INPUT
].
raw
)
break
}
const
GAS_MARGIN
=
ethers
.
utils
.
bigNumberify
(
1000
)
const
estimatedGasLimit
=
await
estimate
(...
args
,
{
value
}).
catch
(
e
=>
{
console
.
log
(
'
error getting gas limit
'
)
})
...
...
@@ -433,71 +499,267 @@ export default function ExchangePage() {
.
then
(
response
=>
{
setTxHash
(
response
)
addTransaction
(
response
)
toggel
PendingConfirmation
(
false
)
set
PendingConfirmation
(
false
)
})
.
catch
(
e
=>
{
console
.
log
(
'
error when trying transaction
'
)
console
.
log
(
e
)
addPopup
(
<
AutoColumn
gap=
"10px"
>
<
Text
>
Transaction Failed: try again.
</
Text
>
</
AutoColumn
>
)
resetModal
()
setShowConfirm
(
false
)
})
}
// errors
const
[
inputError
,
setInputError
]
=
useState
()
const
[
outputError
,
setOutputError
]
=
useState
()
const
[
errorText
,
setErrorText
]
=
useState
(
'
'
)
const
[
isError
,
setIsError
]
=
useState
(
false
)
const
[
inputError
,
setInputError
]
=
useState
(
''
)
const
[
outputError
,
setOutputError
]
=
useState
(
''
)
const
[
isValid
,
setIsValid
]
=
useState
(
false
)
// update errors live
useEffect
(()
=>
{
// reset errors
setInputError
(
null
)
setOutputError
(
null
)
setIsError
(
false
)
setTradeError
(
null
)
setIsValid
(
true
)
if
(
parsedAmounts
[
Field
.
INPUT
]
&&
exchange
&&
JSBI
.
greaterThan
(
parsedAmounts
[
Field
.
INPUT
].
raw
,
exchange
.
reserveOf
(
tokens
[
Field
.
INPUT
]).
raw
)
)
{
setTradeError
(
'
Low Liquidity Error
'
)
setIsValid
(
false
)
}
if
(
parsedAmounts
[
Field
.
OUTPUT
]
&&
exchange
&&
JSBI
.
greaterThan
(
parsedAmounts
[
Field
.
OUTPUT
].
raw
,
exchange
.
reserveOf
(
tokens
[
Field
.
OUTPUT
]).
raw
)
)
{
setTradeError
(
'
Low Liquidity Error
'
)
setIsValid
(
false
)
}
if
(
showInputUnlock
)
{
setInputError
(
'
Need to approve amount on input.
'
)
setInputError
(
'
Approval Needed
'
)
setIsValid
(
false
)
}
if
(
showOutputUnlock
)
{
setOutputError
(
'
Approval Needed
'
)
setIsValid
(
false
)
}
if
(
userBalances
[
Field
.
INPUT
]
&&
parsedAmounts
[
Field
.
INPUT
]
&&
JSBI
.
lessThan
(
userBalances
[
Field
.
INPUT
].
raw
,
parsedAmounts
[
Field
.
INPUT
]?.
raw
)
)
{
setInputError
(
'
Insufficient balance.
'
)
setIs
Error
(
tru
e
)
setIs
Valid
(
fals
e
)
}
},
[
parsedAmounts
,
showInputUnlock
,
userBalances
])
// set error text based on all errors
useEffect
(()
=>
{
setErrorText
(
null
)
if
(
!
parsedAmounts
[
Field
.
INPUT
])
{
setErrorText
(
'
Enter an amount to continue
'
)
}
else
if
(
outputError
)
{
setErrorText
(
outputError
)
}
else
if
(
inputError
)
{
setErrorText
(
inputError
)
return
if
(
userBalances
[
Field
.
OUTPUT
]
&&
parsedAmounts
[
Field
.
OUTPUT
]
&&
JSBI
.
lessThan
(
userBalances
[
Field
.
OUTPUT
].
raw
,
parsedAmounts
[
Field
.
OUTPUT
]?.
raw
)
)
{
setOutputError
(
'
Insufficient balance.
'
)
setIsValid
(
false
)
}
},
[
inputError
,
outputError
,
parsedAmounts
])
},
[
exchange
,
parsedAmounts
,
showInputUnlock
,
showOutputUnlock
,
tokens
,
userBalances
])
const
warningMedium
=
slippageFromTrade
&&
parseFloat
(
slippageFromTrade
.
toFixed
(
4
))
>
ALLOWED_IMPACT_MEDIUM
/
100
const
warningHigh
=
slippageFromTrade
&&
parseFloat
(
slippageFromTrade
.
toFixed
(
4
))
>
ALLOWED_IMPACT_HIGH
/
100
function
resetModal
()
{
setPendingConfirmation
(
true
)
setAttemptingTxn
(
false
)
setShowAdvanced
(
false
)
}
function
modalHeader
()
{
return
(
<
AutoColumn
gap=
{
'
20px
'
}
style=
{
{
marginTop
:
'
40px
'
}
}
>
<
RowBetween
align=
"flex-end"
>
<
Text
fontSize=
{
36
}
fontWeight=
{
500
}
>
{
!!
slippageAdjustedAmounts
[
Field
.
INPUT
]
&&
slippageAdjustedAmounts
[
Field
.
INPUT
].
toSignificant
(
6
)
}
</
Text
>
<
RowFixed
gap=
"10px"
>
<
TokenLogo
address=
{
tokens
[
Field
.
INPUT
]?.
address
}
size=
{
'
24px
'
}
/>
<
Text
fontSize=
{
24
}
fontWeight=
{
500
}
style=
{
{
marginLeft
:
'
10px
'
}
}
>
{
tokens
[
Field
.
INPUT
]?.
symbol
||
''
}
</
Text
>
</
RowFixed
>
</
RowBetween
>
<
RowFixed
>
<
ArrowDown
size=
"16"
color=
"#888D9B"
/>
</
RowFixed
>
<
RowBetween
align=
"flex-end"
>
<
Text
fontSize=
{
36
}
fontWeight=
{
500
}
color=
{
warningHigh
?
'
#FF6871
'
:
'
#2172E5
'
}
>
{
!!
slippageAdjustedAmounts
[
Field
.
OUTPUT
]
&&
slippageAdjustedAmounts
[
Field
.
OUTPUT
].
toSignificant
(
6
)
}
</
Text
>
<
RowFixed
gap=
"10px"
>
<
TokenLogo
address=
{
tokens
[
Field
.
OUTPUT
]?.
address
}
size=
{
'
24px
'
}
/>
<
Text
fontSize=
{
24
}
fontWeight=
{
500
}
style=
{
{
marginLeft
:
'
10px
'
}
}
>
{
tokens
[
Field
.
OUTPUT
]?.
symbol
||
''
}
</
Text
>
</
RowFixed
>
</
RowBetween
>
</
AutoColumn
>
)
}
// error state for button
const
isValid
=
!
errorText
function
modalBottom
()
{
return
showAdvanced
?
(
<
AutoColumn
gap=
"20px"
>
<
Link
onClick=
{
()
=>
{
setShowAdvanced
(
false
)
}
}
>
back
</
Link
>
<
RowBetween
>
<
TYPE
.
main
>
Limit additional price slippage
</
TYPE
.
main
>
<
QuestionHelper
text=
""
/>
</
RowBetween
>
<
Row
>
<
ButtonRadio
active=
{
SLIPPAGE_INDEX
[
1
]
===
activeIndex
}
padding=
"4px 6px"
borderRadius=
"8px"
style=
{
{
marginRight
:
'
16px
'
}
}
width=
{
'
60px
'
}
onClick=
{
()
=>
{
setActiveIndex
(
SLIPPAGE_INDEX
[
1
])
setAllowedSlippage
(
10
)
}
}
>
0.1%
</
ButtonRadio
>
<
ButtonRadio
active=
{
SLIPPAGE_INDEX
[
2
]
===
activeIndex
}
padding=
"4px 6px"
borderRadius=
"8px"
style=
{
{
marginRight
:
'
16px
'
}
}
width=
{
'
60px
'
}
onClick=
{
()
=>
{
setActiveIndex
(
SLIPPAGE_INDEX
[
2
])
setAllowedSlippage
(
100
)
}
}
>
1%
</
ButtonRadio
>
<
ButtonRadio
active=
{
SLIPPAGE_INDEX
[
3
]
===
activeIndex
}
padding=
"4px"
borderRadius=
"8px"
width=
{
'
140px
'
}
onClick=
{
()
=>
{
setActiveIndex
(
SLIPPAGE_INDEX
[
3
])
setAllowedSlippage
(
200
)
}
}
>
2% (suggested)
</
ButtonRadio
>
</
Row
>
<
RowFixed
>
<
InputWrapper
active=
{
SLIPPAGE_INDEX
[
4
]
===
activeIndex
}
>
<
NumericalInput
align=
{
customSlippage
?
'
right
'
:
'
left
'
}
value=
{
customSlippage
||
''
}
onUserInput=
{
val
=>
{
parseCustomInput
(
val
)
setActiveIndex
(
SLIPPAGE_INDEX
[
4
])
}
}
placeHolder=
"Custom"
onClick=
{
()
=>
{
setActiveIndex
(
SLIPPAGE_INDEX
[
4
])
if
(
customSlippage
)
{
parseCustomInput
(
customSlippage
)
}
}
}
/>
%
</
InputWrapper
>
</
RowFixed
>
<
RowBetween
>
<
TYPE
.
main
>
Adjust deadline (minutes from now)
</
TYPE
.
main
>
</
RowBetween
>
<
RowFixed
>
<
NumericalInput
value=
{
customDeadline
}
onUserInput=
{
val
=>
{
parseCustomDeadline
(
val
)
}
}
/>
</
RowFixed
>
</
AutoColumn
>
)
:
(
<>
<
RowBetween
>
<
Text
color=
"#565A69"
fontWeight=
{
500
}
fontSize=
{
16
}
>
Price
</
Text
>
<
Text
fontWeight=
{
500
}
fontSize=
{
16
}
>
{
`1 ${tokens[Field.INPUT]?.symbol} = ${route && route.midPrice && route.midPrice.adjusted.toFixed(8)} ${
tokens[Field.OUTPUT]?.symbol
}`
}
</
Text
>
</
RowBetween
>
<
RowBetween
>
<
Text
color=
"#565A69"
fontWeight=
{
500
}
fontSize=
{
16
}
>
Slippage
<
Link
onClick=
{
()
=>
setShowAdvanced
(
true
)
}
>
(edit limits)
</
Link
>
</
Text
>
<
ErrorText
warningHigh=
{
warningHigh
}
fontWeight=
{
500
}
>
{
slippageFromTrade
&&
slippageFromTrade
.
toFixed
(
4
)
}
%
</
ErrorText
>
</
RowBetween
>
<
ButtonError
onClick=
{
onSwap
}
error=
{
!!
warningHigh
}
style=
{
{
margin
:
'
10px 0
'
}
}
>
<
Text
fontSize=
{
20
}
fontWeight=
{
500
}
>
{
warningHigh
?
'
Swap Anyway
'
:
'
Swap
'
}
</
Text
>
</
ButtonError
>
<
Text
fontSize=
{
12
}
color=
"#565A69"
textAlign=
"center"
>
{
`Output is estimated. You will receive at least ${slippageAdjustedAmounts[Field.OUTPUT]?.toSignificant(6)} ${
tokens[Field.OUTPUT]?.symbol
} or the transaction will revert.`
}
</
Text
>
<
AutoColumn
justify=
"center"
>
<
Link
onClick=
{
()
=>
{
setShowAdvanced
(
true
)
}
}
>
Advanced Options
</
Link
>
</
AutoColumn
>
</>
)
}
const
pendingText
=
` Swapped
${
parsedAmounts
[
Field
.
INPUT
]?.
toSignificant
(
6
)}
$
{
tokens
[
Field
.
INPUT
]?.
symbol
}
for
$
{
parsedAmounts
[
Field
.
OUTPUT
]?.
toSignificant
(
6
)}
$
{
tokens
[
Field
.
OUTPUT
]?.
symbol
}
`
return (
<>
<
Wrapper
>
<ConfirmationModal
isOpen={showConfirm}
onDismiss={() => {
setTxHash
(
null
)
resetModal(
)
setShowConfirm(false)
}}
amount0=
{
parsedAmounts
[
Field
.
INPUT
]
}
amount1=
{
parsedAmounts
[
Field
.
OUTPUT
]
}
price=
{
route
?.
midPrice
}
transactionType=
{
TRANSACTION_TYPE
.
SWAP
}
contractCall=
{
onSwap
}
attemptingTxn={attemptingTxn}
pendingConfirmation={pendingConfirmation}
hash=
{
txHash
?
txHash
.
hash
:
''
}
hash={txHash ? txHash : ''}
topContent={() => modalHeader()}
bottomContent={modalBottom}
pendingText={pendingText}
title="Confirm Swap"
/>
<AutoColumn gap={'20px'}>
<CurrencyInputPanel
...
...
@@ -509,7 +771,7 @@ export default function ExchangePage() {
}}
atMax={atMaxAmountInput}
token={tokens[Field.INPUT]}
onTokenSelection=
{
onTokenSelection
}
onTokenSelection={
address => onTokenSelection(Field.INPUT, address)
}
title={'Input'}
error={inputError}
exchange={exchange}
...
...
@@ -530,36 +792,69 @@ export default function ExchangePage() {
}}
atMax={atMaxAmountOutput}
token={tokens[Field.OUTPUT]}
onTokenSelection=
{
onTokenSelection
}
onTokenSelection={
address => onTokenSelection(Field.OUTPUT, address)
}
title={'Output'}
error={outputError}
exchange={exchange}
disableUnlock
showUnlock={showOutputUnlock}
/>
<RowBetween>
Rate:
<
div
>
<Text fontWeight={500} color="#565A69">
Price
</Text>
<Text fontWeight={500} color="#565A69">
{exchange
? `
1
$
{
tokens
[
Field
.
INPUT
].
symbol
}
=
$
{
route
?.
midPrice
.
toSignificant
(
6
)}
$
{
tokens
[
Field
.
OUTPUT
].
symbol
}
`
: '-'}
</
div
>
</
Text
>
</RowBetween>
<
ColumnCenter
style=
{
{
height
:
'
20px
'
}
}
>
<
ErrorText
fontSize=
{
12
}
error=
{
isError
}
>
{
errorText
&&
errorText
}
</
ErrorText
>
</
ColumnCenter
>
<
ButtonPrimary
{warningMedium && (
<RowBetween>
<Text fontWeight={500} color="#565A69">
Slippage
</Text>
<ErrorText fontWeight={500} warningMedium={warningMedium} warningHigh={warningHigh}>
{slippageFromTrade.toFixed(4)}%
</ErrorText>
</RowBetween>
)}
<ButtonError
onClick={() => {
setShowConfirm(true)
}}
disabled={!isValid}
error={!!warningHigh}
>
<Text fontSize={20} fontWeight={500}>
Swap
{inputError
? inputError
: outputError
? outputError
: tradeError
? tradeError
: warningHigh
? 'Swap Anyway'
: 'Swap'}
</Text>
</
Button
Primary
>
</Button
Error
>
</AutoColumn>
</>
{warningHigh && (
<FixedBottom>
<GreyCard>
<AutoColumn gap="12px">
<RowBetween>
<Text fontWeight={500}>Slippage Warning</Text>
<QuestionHelper text="" />
</RowBetween>
<Text color="#565A69" lineHeight="145.23%;">
This trade will move the price by {slippageFromTrade.toFixed(2)}%. This pool probably doesn’t have
enough liquidity. Are you sure you want to continue this trade?
</Text>
</AutoColumn>
</GreyCard>
</FixedBottom>
)}
</Wrapper>
)
}
src/components/Header/index.js
View file @
4e2c5c1e
...
...
@@ -17,6 +17,7 @@ import { isMobile } from 'react-device-detect'
import
{
useWeb3React
}
from
'
../../hooks
'
import
{
useAddressBalance
}
from
'
../../contexts/Balances
'
import
{
useWalletModalToggle
,
usePopups
}
from
'
../../contexts/Application
'
import
{
AutoColumn
}
from
'
../Column
'
const
HeaderFrame
=
styled
.
div
`
display: flex;
...
...
@@ -68,7 +69,7 @@ const AccountElement = styled.div`
/* width: 100%; */
`
const
FixedPopupColumn
=
styled
.
div
`
const
FixedPopupColumn
=
styled
(
AutoColumn
)
`
position: absolute;
top: 80px;
right: 20px
...
...
@@ -84,6 +85,15 @@ const StyledClose = styled(X)`
}
`
const
Popup
=
styled
(
Card
)
`
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.04), 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.04);
z-index: 9999;
border-radius: 8px;
padding: 1rem;
background:
${
theme
=>
theme
.
white
}
;
`
export
default
function
Header
()
{
const
{
account
,
chainId
}
=
useWeb3React
()
...
...
@@ -117,13 +127,13 @@ export default function Header() {
<
/AccountElement
>
<
Menu
/>
<
/HeaderElement
>
<
FixedPopupColumn
>
<
FixedPopupColumn
gap
=
"
20px
"
>
{
activePopups
.
map
(
item
=>
{
return
(
<
Card
bg
=
"
white
"
padding
=
{
'
16px
'
}
key
=
{
item
.
key
}
borderRadius
=
{
'
8px
'
}
>
<
Popup
key
=
{
item
.
key
}
>
<
StyledClose
color
=
"
#888D9B
"
onClick
=
{()
=>
removePopup
(
item
.
key
)}
/
>
{
item
.
content
}
<
/
Card
>
<
/
Popup
>
)
})}
<
/FixedPopupColumn
>
...
...
src/components/Menu/index.js
View file @
4e2c5c1e
...
...
@@ -6,7 +6,6 @@ import { ReactComponent as MenuIcon } from '../../assets/images/menu.svg'
import
{
Link
}
from
'
../../theme
'
import
{
darken
,
transparentize
}
from
'
polished
'
import
{
useAdvancedManager
}
from
'
../../contexts/LocalStorage
'
import
Toggle
from
'
react-switch
'
...
...
@@ -140,7 +139,7 @@ const EmojiToggle = styled.span`
export
default
function
Menu
()
{
const
[
isDark
,
toggleDarkMode
]
=
useDarkModeManager
()
const
[
isAdvanced
,
toggleAdvanced
]
=
useState
()
const
[
isAdvanced
,
toggleAdvanced
]
=
useState
(
false
)
const
node
=
useRef
()
const
[
open
,
toggle
]
=
useToggle
(
false
)
...
...
src/components/NumericalInput/index.tsx
View file @
4e2c5c1e
...
...
@@ -11,6 +11,8 @@ const StyledInput = styled.input`
flex: 1 1 auto;
width: 0;
background-color:
${({
theme
})
=>
theme
.
inputBackground
}
;
font-size:
${({
fontSize
})
=>
fontSize
&&
fontSize
}
;
text-align:
${({
align
})
=>
align
&&
align
}
;
[type='number'] {
-moz-appearance: textfield;
...
...
@@ -32,10 +34,10 @@ function escapeRegExp(string: string): string {
return
string
.
replace
(
/
[
.*+?^${}()|[
\]\\]
/g
,
'
\\
$&
'
)
// $& means the whole matched string
}
export
const
Input
=
React
.
memo
(({
field
,
value
,
onUserInput
,
...
rest
}:
any
)
=>
{
export
const
Input
=
React
.
memo
(({
value
,
onUserInput
,
placeHolder
=
null
,
...
rest
}:
any
)
=>
{
function
enforcer
(
nextUserInput
:
string
)
{
if
(
nextUserInput
===
''
||
inputRegex
.
test
(
escapeRegExp
(
nextUserInput
)))
{
onUserInput
(
field
,
nextUserInput
)
onUserInput
(
nextUserInput
)
}
}
...
...
@@ -53,7 +55,7 @@ export const Input = React.memo(({ field, value, onUserInput, ...rest }: any) =>
autoCorrect=
"off"
// text-specific options
type=
"text"
placeholder=
"0.0"
placeholder=
{
placeHolder
||
'
0.0
'
}
minLength=
{
1
}
maxLength=
{
79
}
spellCheck=
"false"
...
...
src/components/PoolFinder/index.js
View file @
4e2c5c1e
...
...
@@ -16,7 +16,7 @@ import { Link } from '../../theme'
import
{
Text
}
from
'
rebass
'
import
{
Plus
}
from
'
react-feather
'
import
{
LightCard
}
from
'
../Card
'
import
{
AutoColumn
,
ColumnCenter
}
from
'
../Column
'
import
Column
,
{
AutoColumn
,
ColumnCenter
}
from
'
../Column
'
import
{
ButtonPrimary
,
ButtonDropwdown
,
ButtonDropwdownLight
}
from
'
../Button
'
import
DoubleTokenLogo
from
'
../DoubleLogo
'
...
...
@@ -112,6 +112,13 @@ function PoolFinder({ history }) {
<
/Row
>
<
/ButtonDropwdownLight
>
)}
{
allowImport
&&
(
<
ColumnCenter
justify
=
"
center
"
style
=
{{
backgroundColor
:
'
#EBF4FF
'
,
padding
:
'
8px
'
,
borderRadius
:
'
12px
'
}}
>
<
Text
textAlign
=
"
center
"
fontWeight
=
{
500
}
color
=
"
#2172E5
"
>
Liquidity
Found
!
<
/Text
>
<
/ColumnCenter
>
)}
{
position
?
(
!
JSBI
.
equal
(
position
.
raw
,
JSBI
.
BigInt
(
0
))
?
(
<
PositionCard
...
...
@@ -155,11 +162,7 @@ function PoolFinder({ history }) {
<
/Text
>
<
/LightCard
>
)}
{
allowImport
&&
(
<
Text
textAlign
=
"
center
"
fontWeight
=
{
500
}
>
Liquidity
Found
!
<
/Text
>
)}
<
ButtonPrimary
disabled
=
{
!
allowImport
}
onClick
=
{
endSearch
}
>
<
Text
fontWeight
=
{
500
}
fontSize
=
{
20
}
>
Import
...
...
src/components/PositionCard/index.js
View file @
4e2c5c1e
...
...
@@ -61,11 +61,6 @@ function PositionCard({ exchangeAddress, token0, token1, history, minimal = fals
return
(
<
DynamicCard
{...
rest
}
>
<
AutoColumn
gap
=
"
20px
"
>
<
FixedHeightRow
>
<
Text
fontWeight
=
{
500
}
fontSize
=
{
16
}
>
Current
Position
<
/Text
>
<
/FixedHeightRow
>
<
FixedHeightRow
>
<
RowFixed
>
<
DoubleLogo
a0
=
{
token0
?.
address
||
''
}
a1
=
{
token1
?.
address
||
''
}
margin
=
{
true
}
size
=
{
24
}
/
>
...
...
src/components/Row/index.js
View file @
4e2c5c1e
...
...
@@ -4,6 +4,7 @@ const Row = styled.div`
width: 100%;
display: flex;
align-items: center;
align-items:
${({
align
})
=>
align
&&
align
}
;
`
export
const
RowBetween
=
styled
(
Row
)
`
...
...
src/components/SearchModal/index.js
View file @
4e2c5c1e
...
...
@@ -411,16 +411,18 @@ function SearchModal({ history, isOpen, onDismiss, onTokenSelect, urlAddedTokens
/
>
<
RowBetween
>
<
div
>
<
Text
>
Don
'
t see a pool?{
'
'
}
<StyledLink
onClick={() => {
history.push(
'
/
find
'
)
}}
>
Import it.
</StyledLink>
</Text>
{
filterType
!==
'
tokens
'
&&
(
<
Text
>
Don
'
t see a pool?{
'
'
}
<StyledLink
onClick={() => {
history.push(
'
/
find
'
)
}}
>
Import it.
</StyledLink>
</Text>
)}
</div>
<div />
<Filter title="Your Balances" filter={FILTERS.BALANCES} />
...
...
src/constants/index.ts
View file @
4e2c5c1e
...
...
@@ -19,13 +19,6 @@ export const SUPPORTED_THEMES = {
LIGHT
:
'
LIGHT
'
}
export
enum
TRANSACTION_TYPE
{
SWAP
,
SEND
,
ADD
,
REMOVE
}
const
MAINNET_WALLETS
=
{
INJECTED
:
{
connector
:
injected
,
...
...
src/contexts/Exchanges.tsx
View file @
4e2c5c1e
...
...
@@ -9,14 +9,14 @@ import { ChainId, WETH, Token, TokenAmount, Exchange, JSBI } from '@uniswap/sdk'
const
UPDATE
=
'
UPDATE
'
const
ALL_EXCHANGES
:
[
Token
,
Token
][]
=
[
//
[
//
INITIAL_TOKENS_CONTEXT[ChainId.RINKEBY][WETH[ChainId.RINKEBY].address],
//
INITIAL_TOKENS_CONTEXT[ChainId.RINKEBY]['0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735']
// ]
//
[
//
INITIAL_TOKENS_CONTEXT[ChainId.RINKEBY]['0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735'],
//
INITIAL_TOKENS_CONTEXT[ChainId.RINKEBY]['0x8ab15C890E5C03B5F240f2D146e3DF54bEf3Df44']
//
]
[
INITIAL_TOKENS_CONTEXT
[
ChainId
.
RINKEBY
][
WETH
[
ChainId
.
RINKEBY
].
address
],
INITIAL_TOKENS_CONTEXT
[
ChainId
.
RINKEBY
][
'
0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735
'
]
],
[
INITIAL_TOKENS_CONTEXT
[
ChainId
.
RINKEBY
][
'
0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735
'
],
INITIAL_TOKENS_CONTEXT
[
ChainId
.
RINKEBY
][
'
0x8ab15C890E5C03B5F240f2D146e3DF54bEf3Df44
'
]
]
]
const
EXCHANGE_MAP
:
{
...
...
src/pages/App.js
View file @
4e2c5c1e
...
...
@@ -27,11 +27,6 @@ const HeaderWrapper = styled.div`
width: 100%;
justify-content: space-between;
`
const
FooterWrapper
=
styled
.
div
`
width: 100%;
min-height: 30px;
align-self: flex-end;
`
const
BodyWrapper
=
styled
.
div
`
display: flex;
...
...
@@ -50,7 +45,7 @@ const Body = styled.div`
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: 20px;
padding: 2rem 2rem;
padding: 2rem 2rem
1rem 2rem
;
`
export
default
function
App
()
{
...
...
src/pages/Supply/AddLiquidity.tsx
View file @
4e2c5c1e
...
...
@@ -5,26 +5,27 @@ import { parseUnits, parseEther } from '@ethersproject/units'
import
{
WETH
,
TokenAmount
,
JSBI
,
Percent
,
Route
}
from
'
@uniswap/sdk
'
import
DoubleLogo
from
'
../../components/DoubleLogo
'
import
TokenLogo
from
'
../../components/TokenLogo
'
import
SearchModal
from
'
../../components/SearchModal
'
import
PositionCard
from
'
../../components/PositionCard
'
import
ConfirmationModal
from
'
../../components/ConfirmationModal
'
import
CurrencyInputPanel
from
'
../../components/CurrencyInputPanel
'
import
{
Text
}
from
'
rebass
'
import
{
Plus
}
from
'
react-feather
'
import
{
RowBetween
}
from
'
../../components/Row
'
import
{
ChevronDown
}
from
'
react-feather
'
import
{
ButtonPrimary
}
from
'
../../components/Button
'
import
{
AutoColumn
,
ColumnCenter
}
from
'
../../components/Column
'
import
{
ButtonPrimary
,
ButtonEmpty
}
from
'
../../components/Button
'
import
Row
,
{
RowBetween
,
RowFlat
,
RowFixed
}
from
'
../../components/Row
'
import
{
useToken
}
from
'
../../contexts/Tokens
'
import
{
useWeb3React
}
from
'
../../hooks
'
import
{
usePopups
}
from
'
../../contexts/Application
'
import
{
useAddressBalance
}
from
'
../../contexts/Balances
'
import
{
useAddressAllowance
}
from
'
../../contexts/Allowances
'
import
{
useTransactionAdder
}
from
'
../../contexts/Transactions
'
import
{
useExchange
,
useTotalSupply
}
from
'
../../contexts/Exchanges
'
import
{
BigNumber
}
from
'
ethers/utils
'
import
{
TRANSACTION_TYPE
,
ROUTER_ADDRESSES
}
from
'
../../constants
'
import
{
ROUTER_ADDRESSES
}
from
'
../../constants
'
import
{
getRouterContract
,
calculateGasMargin
}
from
'
../../utils
'
// denominated in bips
...
...
@@ -41,7 +42,7 @@ const Wrapper = styled.div`
const
FixedBottom
=
styled
.
div
`
position: absolute;
bottom: -2
4
0px;
bottom: -2
0
0px;
width: 100%;
`
...
...
@@ -145,6 +146,7 @@ export default function AddLiquidity({ token0, token1 }) {
// modal states
const
[
showSearch
,
setShowSearch
]
=
useState
<
boolean
>
(
false
)
const
[
showConfirm
,
setShowConfirm
]
=
useState
<
boolean
>
(
false
)
const
[
attemptingTxn
,
setAttemptingTxn
]
=
useState
<
boolean
>
(
false
)
// clicke confirm
const
[
pendingConfirmation
,
setPendingConfirmation
]
=
useState
<
boolean
>
(
true
)
// input state
...
...
@@ -369,7 +371,10 @@ export default function AddLiquidity({ token0, token1 }) {
return
null
}
const
[,
addPopup
]
=
usePopups
()
async
function
onAdd
()
{
setAttemptingTxn
(
true
)
const
router
=
getRouterContract
(
chainId
,
library
,
account
)
const
minTokenInput
=
calculateSlippageAmount
(
parsedAmounts
[
Field
.
INPUT
])[
0
]
...
...
@@ -428,26 +433,112 @@ export default function AddLiquidity({ token0, token1 }) {
})
.
catch
((
e
:
Error
)
=>
{
console
.
log
(
e
)
addPopup
(
<
AutoColumn
gap=
"10px"
>
<
Text
>
Transaction Failed: try again.
</
Text
>
</
AutoColumn
>
)
setPendingConfirmation
(
true
)
setAttemptingTxn
(
false
)
setShowConfirm
(
false
)
})
}
const
modalHeader
=
()
=>
{
return
(
<
AutoColumn
gap=
"20px"
>
<
RowFlat
style=
{
{
marginTop
:
'
60px
'
}
}
>
<
Text
fontSize=
"48px"
fontWeight=
{
500
}
lineHeight=
"32px"
marginRight=
{
10
}
>
{
liquidityMinted
?.
toFixed
(
6
)
}
</
Text
>
<
DoubleLogo
a0=
{
tokens
[
Field
.
INPUT
]?.
symbol
||
''
}
a1=
{
tokens
[
Field
.
OUTPUT
]?.
symbol
||
''
}
size=
{
30
}
/>
</
RowFlat
>
<
Row
>
<
Text
fontSize=
"24px"
>
{
tokens
[
Field
.
INPUT
]?.
symbol
+
'
:
'
+
tokens
[
Field
.
OUTPUT
]?.
symbol
+
'
Pool Tokens
'
}
</
Text
>
</
Row
>
</
AutoColumn
>
)
}
const
modalBottom
=
()
=>
{
return
(
<>
<
RowBetween
>
<
Text
color=
"#565A69"
fontWeight=
{
500
}
fontSize=
{
16
}
>
{
tokens
[
Field
.
INPUT
]?.
symbol
}
Deposited
</
Text
>
<
RowFixed
>
<
TokenLogo
address=
{
tokens
[
Field
.
INPUT
]?.
address
||
''
}
style=
{
{
marginRight
:
'
8px
'
}
}
/>
<
Text
fontWeight=
{
500
}
fontSize=
{
16
}
>
{
!!
parsedAmounts
[
Field
.
INPUT
]
&&
parsedAmounts
[
Field
.
INPUT
].
toSignificant
(
6
)
}
</
Text
>
</
RowFixed
>
</
RowBetween
>
<
RowBetween
>
<
Text
color=
"#565A69"
fontWeight=
{
500
}
fontSize=
{
16
}
>
{
tokens
[
Field
.
OUTPUT
]?.
symbol
}
Deposited
</
Text
>
<
RowFixed
>
<
TokenLogo
address=
{
tokens
[
Field
.
OUTPUT
]?.
address
||
''
}
style=
{
{
marginRight
:
'
8px
'
}
}
/>
<
Text
fontWeight=
{
500
}
fontSize=
{
16
}
>
{
!!
parsedAmounts
[
Field
.
OUTPUT
]
&&
parsedAmounts
[
Field
.
OUTPUT
].
toSignificant
(
6
)
}
</
Text
>
</
RowFixed
>
</
RowBetween
>
<
RowBetween
>
<
Text
color=
"#565A69"
fontWeight=
{
500
}
fontSize=
{
16
}
>
Rate
</
Text
>
<
Text
fontWeight=
{
500
}
fontSize=
{
16
}
>
{
`1 ${tokens[Field.INPUT]?.symbol} = ${route?.midPrice &&
route?.midPrice?.raw?.denominator &&
route.midPrice.adjusted.toFixed(8)} ${tokens[Field.OUTPUT]?.symbol}`
}
</
Text
>
</
RowBetween
>
<
RowBetween
>
<
Text
color=
"#565A69"
fontWeight=
{
500
}
fontSize=
{
16
}
>
Minted Pool Share:
</
Text
>
<
Text
fontWeight=
{
500
}
fontSize=
{
16
}
>
{
poolTokenPercentage
?.
toFixed
(
6
)
+
'
%
'
}
</
Text
>
</
RowBetween
>
<
ButtonPrimary
style=
{
{
margin
:
'
20px 0
'
}
}
onClick=
{
onAdd
}
>
<
Text
fontWeight=
{
500
}
fontSize=
{
20
}
>
Confirm Supply
</
Text
>
</
ButtonPrimary
>
<
Text
fontSize=
{
12
}
color=
"#565A69"
textAlign=
"center"
>
{
`Output is estimated. You will receive at least ${liquidityMinted?.toFixed(6)} UNI ${
tokens[Field.INPUT]?.symbol
}/${tokens[Field.OUTPUT]?.symbol} or the transaction will revert.`
}
</
Text
>
</>
)
}
const
pendingText
=
`Supplied
${
parsedAmounts
[
Field
.
INPUT
]?.
toSignificant
(
6
)}
$
{
tokens
[
Field
.
INPUT
]?.
symbol
}
$
{
'
and
'
}
$
{
parsedAmounts
[
Field
.
OUTPUT
]?.
toSignificant
(
6
)}
$
{
tokens
[
Field
.
OUTPUT
]?.
symbol
}
`
return (
<Wrapper>
<ConfirmationModal
isOpen={showConfirm}
onDismiss={() => {
setPendingConfirmation(true)
setAttemptingTxn(false)
setShowConfirm(false)
}}
liquidityAmount=
{
liquidityMinted
}
amount0=
{
parsedAmounts
[
Field
.
INPUT
]
}
amount1=
{
parsedAmounts
[
Field
.
OUTPUT
]
}
poolTokenPercentage=
{
poolTokenPercentage
}
price=
{
route
?.
midPrice
&&
route
?.
midPrice
?.
raw
?.
denominator
}
transactionType=
{
TRANSACTION_TYPE
.
ADD
}
contractCall=
{
onAdd
}
attemptingTxn={attemptingTxn}
pendingConfirmation={pendingConfirmation}
hash={txHash ? txHash : ''}
topContent={() => modalHeader()}
bottomContent={modalBottom}
pendingText={pendingText}
title="You will receive"
/>
<SearchModal
isOpen={showSearch}
...
...
@@ -456,22 +547,6 @@ export default function AddLiquidity({ token0, token1 }) {
}}
/>
<AutoColumn gap="20px">
<
ButtonEmpty
padding=
{
'
1rem
'
}
onClick=
{
()
=>
{
setShowSearch
(
true
)
}
}
>
<
RowBetween
>
<
DoubleLogo
a0=
{
exchange
?.
token0
?.
address
||
''
}
a1=
{
exchange
?.
token1
?.
address
||
''
}
size=
{
24
}
/>
<
Text
fontSize=
{
20
}
>
{
exchange
?.
token0
&&
exchange
?.
token1
?
exchange
.
token0
.
symbol
+
'
/
'
+
exchange
.
token1
.
symbol
+
'
Pool
'
:
''
}
</
Text
>
<
ChevronDown
size=
{
24
}
/>
</
RowBetween
>
</
ButtonEmpty
>
{noLiquidity && (
<ColumnCenter>
<Text fontWeight={500} style={{ textAlign: 'center' }}>
...
...
src/pages/Supply/RemoveLiquidity.tsx
View file @
4e2c5c1e
...
...
@@ -6,15 +6,17 @@ import { TokenAmount, JSBI, Route, WETH, Percent } from '@uniswap/sdk'
import
Slider
from
'
../../components/Slider
'
import
TokenLogo
from
'
../../components/TokenLogo
'
import
DoubleLogo
from
'
../../components/DoubleLogo
'
import
PositionCard
from
'
../../components/PositionCard
'
import
ConfirmationModal
from
'
../../components/ConfirmationModal
'
import
CurrencyInputPanel
from
'
../../components/CurrencyInputPanel
'
import
{
Text
}
from
'
rebass
'
import
{
LightCard
}
from
'
../../components/Card
'
import
{
ButtonPrimary
}
from
'
../../components/Button
'
import
{
ButtonConfirmed
}
from
'
../../components/Button
'
import
{
ArrowDown
,
Plus
}
from
'
react-feather
'
import
{
RowBetween
,
RowFixed
}
from
'
../../components/Row
'
import
{
AutoColumn
,
ColumnCenter
}
from
'
../../components/Column
'
import
Row
,
{
RowBetween
,
RowFixed
}
from
'
../../components/Row
'
import
{
useToken
}
from
'
../../contexts/Tokens
'
import
{
useWeb3React
}
from
'
../../hooks
'
...
...
@@ -25,7 +27,6 @@ import { useExchange, useTotalSupply } from '../../contexts/Exchanges'
import
{
BigNumber
}
from
'
ethers/utils
'
import
{
splitSignature
}
from
'
@ethersproject/bytes
'
import
{
TRANSACTION_TYPE
}
from
'
../../constants
'
import
{
ROUTER_ADDRESSES
}
from
'
../../constants
'
import
{
getRouterContract
,
calculateGasMargin
}
from
'
../../utils
'
...
...
@@ -138,6 +139,10 @@ function reducer(
}
}
const
ConfirmedText
=
styled
(
Text
)
`
color:
${({
theme
,
confirmed
})
=>
(
confirmed
?
theme
.
connectedGreen
:
theme
.
white
)}
;
`
export
default
function
RemoveLiquidity
({
token0
,
token1
})
{
const
{
account
,
chainId
,
library
}
=
useWeb3React
()
const
routerAddress
=
ROUTER_ADDRESSES
[
chainId
]
...
...
@@ -302,19 +307,19 @@ export default function RemoveLiquidity({ token0, token1 }) {
:
false
// errors
const
[
generalError
,
setGeneralError
]
=
useState
()
const
[
inputError
,
setInputError
]
=
useState
()
const
[
outputError
,
setOutputError
]
=
useState
()
const
[
poolTokenError
,
setPoolTokenError
]
=
useState
()
const
[
generalError
,
setGeneralError
]
=
useState
(
''
)
const
[
inputError
,
setInputError
]
=
useState
(
''
)
const
[
outputError
,
setOutputError
]
=
useState
(
''
)
const
[
poolTokenError
,
setPoolTokenError
]
=
useState
(
''
)
const
[
isValid
,
setIsValid
]
=
useState
(
false
)
// update errors live
useEffect
(()
=>
{
// reset errors
setGeneralError
(
null
)
setInputError
(
null
)
setOutputError
(
null
)
setPoolTokenError
(
null
)
setGeneralError
(
''
)
setInputError
(
''
)
setOutputError
(
''
)
setPoolTokenError
(
''
)
setIsValid
(
true
)
if
(
formattedAmounts
[
Field
.
TOKEN0
]
===
''
)
{
...
...
@@ -480,28 +485,103 @@ export default function RemoveLiquidity({ token0, token1 }) {
setPendingConfirmation
(
true
)
}
function
modalHeader
()
{
return
(
<
AutoColumn
gap=
"16px"
>
<
Row
style=
{
{
marginTop
:
'
40px
'
}
}
>
<
TokenLogo
address=
{
tokens
[
Field
.
TOKEN0
]?.
symbol
}
size=
{
'
30px
'
}
/>
<
Text
fontSize=
"24px"
marginLeft=
{
10
}
>
{
tokens
[
Field
.
TOKEN0
]?.
symbol
}{
'
'
}
{
!!
parsedAmounts
[
Field
.
TOKEN0
]
&&
parsedAmounts
[
Field
.
TOKEN0
].
toSignificant
(
8
)
}
</
Text
>
</
Row
>
<
Row
>
<
TokenLogo
address=
{
tokens
[
Field
.
TOKEN1
]?.
symbol
}
size=
{
'
30px
'
}
/>
<
Text
fontSize=
"24px"
marginLeft=
{
10
}
>
{
tokens
[
Field
.
TOKEN1
]?.
symbol
}{
'
'
}
{
!!
parsedAmounts
[
Field
.
TOKEN1
]
&&
parsedAmounts
[
Field
.
TOKEN1
].
toSignificant
(
8
)
}
</
Text
>
</
Row
>
</
AutoColumn
>
)
}
function
modalBottom
()
{
return
(
<>
<
RowBetween
>
<
Text
color=
"#565A69"
fontWeight=
{
500
}
fontSize=
{
16
}
>
{
'
UNI
'
+
tokens
[
Field
.
TOKEN0
]?.
symbol
+
'
:
'
+
tokens
[
Field
.
TOKEN1
]?.
symbol
}
Burned
</
Text
>
<
RowFixed
>
<
DoubleLogo
a0=
{
tokens
[
Field
.
TOKEN0
]?.
address
||
''
}
a1=
{
tokens
[
Field
.
TOKEN1
]?.
address
||
''
}
margin=
{
true
}
/>
<
Text
fontWeight=
{
500
}
fontSize=
{
16
}
>
{
parsedAmounts
[
Field
.
LIQUIDITY
]?.
toSignificant
(
6
)
}
</
Text
>
</
RowFixed
>
</
RowBetween
>
<
RowBetween
>
<
Text
color=
"#565A69"
fontWeight=
{
500
}
fontSize=
{
16
}
>
Rate
</
Text
>
<
Text
fontWeight=
{
500
}
fontSize=
{
16
}
>
{
`1 ${tokens[Field.TOKEN0]?.symbol} = ${route?.midPrice && route.midPrice.adjusted.toFixed(8)} ${
tokens[Field.TOKEN1]?.symbol
}`
}
</
Text
>
</
RowBetween
>
<
RowBetween
gap=
"20px"
>
<
ButtonConfirmed
style=
{
{
margin
:
'
20px 0
'
}
}
width=
"48%"
onClick=
{
onSign
}
confirmed=
{
signed
}
disabled=
{
signed
}
>
<
ConfirmedText
fontWeight=
{
500
}
fontSize=
{
20
}
confirmed=
{
signed
}
>
{
signed
?
'
Signed
'
:
'
Sign
'
}
</
ConfirmedText
>
</
ButtonConfirmed
>
<
ButtonPrimary
width=
"48%"
disabled=
{
!
signed
}
style=
{
{
margin
:
'
20px 0
'
}
}
onClick=
{
onRemove
}
>
<
Text
fontWeight=
{
500
}
fontSize=
{
20
}
>
Confirm Remove
</
Text
>
</
ButtonPrimary
>
</
RowBetween
>
<
Text
fontSize=
{
12
}
color=
"#565A69"
textAlign=
"center"
>
{
`Output is estimated. You will receive at least ${parsedAmounts[Field.TOKEN0]?.toFixed(6)} ${
tokens[Field.TOKEN0]?.symbol
} and at least ${parsedAmounts[Field.TOKEN1]?.toFixed(6)} ${
tokens[Field.TOKEN1]?.symbol
} or the transaction will revert.`
}
</
Text
>
</>
)
}
const
pendingText
=
`Removed
${
parsedAmounts
[
Field
.
TOKEN0
]?.
toSignificant
(
6
)}
$
{
tokens
[
Field
.
TOKEN0
]?.
symbol
}
and
$
{
parsedAmounts
[
Field
.
TOKEN1
]?.
toSignificant
(
6
)}
$
{
tokens
[
Field
.
TOKEN1
]?.
symbol
}
`
return (
<Wrapper>
{
!!
parsedAmounts
[
Field
.
TOKEN0
]
&&
!!
parsedAmounts
[
Field
.
TOKEN1
]
&&
!!
parsedAmounts
[
Field
.
LIQUIDITY
]
&&
(
<
ConfirmationModal
isOpen=
{
showConfirm
}
onDismiss=
{
()
=>
{
resetModalState
()
setShowConfirm
(
false
)
}
}
amount0=
{
parsedAmounts
[
Field
.
TOKEN0
]
}
amount1=
{
parsedAmounts
[
Field
.
TOKEN1
]
}
price=
{
route
?.
midPrice
}
liquidityAmount=
{
parsedAmounts
[
Field
.
LIQUIDITY
]
}
transactionType=
{
TRANSACTION_TYPE
.
REMOVE
}
contractCall=
{
onRemove
}
extraCall=
{
onSign
}
signed=
{
signed
}
attemptedRemoval=
{
attemptedRemoval
}
pendingConfirmation=
{
pendingConfirmation
}
hash=
{
txHash
?
txHash
:
''
}
/>
)
}
<ConfirmationModal
isOpen={showConfirm}
onDismiss={() => {
resetModalState()
setShowConfirm(false)
}}
attemptingTxn={attemptedRemoval}
pendingConfirmation={pendingConfirmation}
hash={txHash ? txHash : ''}
topContent={modalHeader}
bottomContent={modalBottom}
pendingText={pendingText}
title="You will remove"
/>
<AutoColumn gap="20px">
<LightCard>
<AutoColumn gap="20px">
...
...
src/pages/Supply/index.js
View file @
4e2c5c1e
...
...
@@ -3,22 +3,22 @@ import styled from 'styled-components'
import
{
JSBI
}
from
'
@uniswap/sdk
'
import
{
withRouter
}
from
'
react-router-dom
'
import
{
useWeb3React
}
from
'
@web3-react/core
'
import
{
useAllTokens
}
from
'
../../contexts/Tokens
'
import
{
useAllExchanges
}
from
'
../../contexts/Exchanges
'
import
{
useAllBalances
,
useAccountLPBalances
}
from
'
../../contexts/Balances
'
import
Card
from
'
../../components/Card
'
import
Question
from
'
../../components/Question
'
import
SearchModal
from
'
../../components/SearchModal
'
import
PositionCard
from
'
../../components/PositionCard
'
import
Row
,
{
RowBetween
}
from
'
../../components/Row
'
import
Card
,
{
LightCard
}
from
'
../../components/Card
'
import
{
Link
}
from
'
../../theme
'
import
{
Text
}
from
'
rebass
'
import
{
AutoColumn
}
from
'
../../components/Column
'
import
{
ArrowRight
}
from
'
react-feather
'
import
{
ButtonPrimary
}
from
'
../../components/Button
'
import
{
useWeb3React
}
from
'
@web3-react/core
'
import
{
useAllTokens
}
from
'
../../contexts/Tokens
'
import
{
useAllExchanges
}
from
'
../../contexts/Exchanges
'
import
{
useAllBalances
,
useAccountLPBalances
}
from
'
../../contexts/Balances
'
const
Positions
=
styled
.
div
`
position: relative;
margin-top: 38px;
...
...
@@ -71,19 +71,16 @@ function Supply({ history }) {
<
/ButtonPrimary
>
<
Positions
>
<
AutoColumn
gap
=
"
20px
"
>
<
RowBetween
>
<
Text
fontWeight
=
{
500
}
>
Your
Pooled
Liquidity
<
/Text
>
<
Question
text
=
"
filler text
"
/>
<
/RowBetween
>
{
filteredExchangeList
}
{
filteredExchangeList
?.
length
===
0
&&
(
<
LightCard
bg
=
"
rgba(255, 255, 255, 0.6)
"
padding
=
{
'
45px
'
}
>
<
Text
color
=
"
#C3C5CB
"
>
Add
liquidity
to
see
your
positions
<
/Text
>
<
/LightCard
>
{
filteredExchangeList
?.
length
!==
0
&&
(
<
RowBetween
>
<
Text
fontWeight
=
{
500
}
>
Your
Pooled
Liquidity
<
/Text
>
<
Question
text
=
"
filler text
"
/>
<
/RowBetween
>
)}
{
filteredExchangeList
}
<
AutoColumn
justify
=
"
center
"
>
<
Text
color
=
"
#AEAEAE
"
>
Already
have
liquidity
?{
'
'
}
{
filteredExchangeList
?.
length
!==
0
?
`Don't see your `
:
'
Already have
'
}
liquidity
?{
'
'
}
<
Link
onClick
=
{()
=>
{
history
.
push
(
'
/find
'
)
...
...
src/theme/components.js
View file @
4e2c5c1e
...
...
@@ -45,6 +45,7 @@ export const Link = styled.a.attrs({
text-decoration: none;
cursor: pointer;
color:
${({
theme
})
=>
theme
.
royalBlue
}
;
font-weight: 500;
:focus {
outline: none;
...
...
src/theme/index.js
View file @
4e2c5c1e
...
...
@@ -3,6 +3,7 @@ import { ThemeProvider as StyledComponentsThemeProvider, createGlobalStyle, css
import
{
getQueryParam
,
checkSupportedTheme
}
from
'
../utils
'
import
{
SUPPORTED_THEMES
}
from
'
../constants
'
import
{
useDarkModeManager
}
from
'
../contexts/LocalStorage
'
import
{
Text
}
from
'
rebass
'
export
*
from
'
./components
'
...
...
@@ -117,6 +118,19 @@ const theme = darkMode => ({
`
})
export
const
TYPE
=
{
main
:
({
children
,
...
rest
})
=>
(
<
Text
fontWeight
=
{
500
}
color
=
{
theme
().
mineshaftGray
}
{...
rest
}
>
{
children
}
<
/Text
>
),
blue
:
({
children
,
...
rest
})
=>
(
<
Text
fontWeight
=
{
500
}
color
=
{
theme
().
royalBlue
}
{...
rest
}
>
{
children
}
<
/Text
>
)
}
export
const
GlobalStyle
=
createGlobalStyle
`
@import url('https://rsms.me/inter/inter.css');
html { font-family: 'Inter', sans-serif; }
...
...
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