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
655b7956
Commit
655b7956
authored
Mar 17, 2020
by
ianlapham
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
basic send added
parent
4e2c5c1e
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
898 additions
and
325 deletions
+898
-325
QR.svg
src/assets/svg/QR.svg
+3
-0
index.js
src/components/Button/index.js
+22
-1
index.js
src/components/CurrencyInputPanel/index.js
+27
-18
index.tsx
src/components/ExchangePage/index.tsx
+514
-248
index.tsx
src/components/NumericalInput/index.tsx
+1
-1
index.js
src/components/SearchModal/index.js
+61
-22
index.js
src/components/Slider/index.js
+1
-1
index.js
src/components/TokenLogo/index.js
+1
-1
index.js
src/pages/Send/index.js
+0
-6
index.tsx
src/pages/Send/index.tsx
+251
-0
RemoveLiquidity.tsx
src/pages/Supply/RemoveLiquidity.tsx
+1
-1
index.js
src/pages/Swap/index.js
+1
-1
index.js
src/theme/index.js
+15
-0
price.js
src/utils/price.js
+0
-25
No files found.
src/assets/svg/QR.svg
0 → 100644
View file @
655b7956
<svg
width=
"21"
height=
"21"
viewBox=
"0 0 21 21"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<path
fill-rule=
"evenodd"
clip-rule=
"evenodd"
d=
"M7.5 1.5H1.5V7.5H7.5V1.5ZM1.5 0H0V1.5V7.5V9H1.5H7.5H9V7.5V1.5V0H7.5H1.5ZM4.5 3H3V4.5V6H4.5H6V4.5V3H4.5ZM1.5 19.5V13.5H7.5V19.5H1.5ZM0 12H1.5H7.5H9V13.5V19.5V21H7.5H1.5H0V19.5V13.5V12ZM4.5 15H3V16.5V18H4.5H6V16.5V15H4.5ZM13.5 1.5H19.5V7.5H13.5V1.5ZM12 0H13.5H19.5H21V1.5V7.5V9H19.5H13.5H12V7.5V1.5V0ZM16.5 3H15V4.5V6H16.5H18V4.5V3H16.5ZM16.5 12H12V21H13.5V16.5H15V18H21V12H19.5V13.5H16.5V12ZM18 19.5H16.5V21H18V19.5ZM19.5 19.5H21V21H19.5V19.5Z"
fill=
"black"
/>
</svg>
src/components/Button/index.js
View file @
655b7956
...
@@ -89,6 +89,27 @@ export const ButtonEmpty = styled(Base)`
...
@@ -89,6 +89,27 @@ export const ButtonEmpty = styled(Base)`
}
}
`
`
export
const
ButtonWhite
=
styled
(
Base
)
`
border: 1px solid #edeef2;
background-color:
${({
theme
})
=>
theme
.
panelBackground
}
;
};
color: black;
&:focus {
box-shadow: 0 0 0 1pt
${({
theme
})
=>
darken
(
0.05
,
'
#edeef2
'
)}
;
}
&:hover {
box-shadow: 0 0 0 1pt
${({
theme
})
=>
darken
(
0.1
,
'
#edeef2
'
)}
;
}
&:active {
box-shadow: 0 0 0 1pt
${({
theme
})
=>
darken
(
0.1
,
'
#edeef2
'
)}
;
}
&:disabled {
opacity: 50%;
cursor: auto;
}
`
const
ButtonConfirmedStyle
=
styled
(
Base
)
`
const
ButtonConfirmedStyle
=
styled
(
Base
)
`
background-color:
${({
theme
})
=>
lighten
(
0.5
,
theme
.
connectedGreen
)}
;
background-color:
${({
theme
})
=>
lighten
(
0.5
,
theme
.
connectedGreen
)}
;
border: 1px solid
${({
theme
})
=>
theme
.
connectedGreen
}
;
border: 1px solid
${({
theme
})
=>
theme
.
connectedGreen
}
;
...
@@ -160,7 +181,7 @@ export function ButtonDropwdownLight({ disabled, children, ...rest }) {
...
@@ -160,7 +181,7 @@ export function ButtonDropwdownLight({ disabled, children, ...rest }) {
export
function
ButtonRadio
({
active
,
children
,
...
rest
})
{
export
function
ButtonRadio
({
active
,
children
,
...
rest
})
{
if
(
!
active
)
{
if
(
!
active
)
{
return
<
Button
Empty
{...
rest
}
>
{
children
}
<
/ButtonEmpty
>
return
<
Button
White
{...
rest
}
>
{
children
}
<
/ButtonWhite
>
}
else
{
}
else
{
return
<
ButtonPrimary
{...
rest
}
>
{
children
}
<
/ButtonPrimary
>
return
<
ButtonPrimary
{...
rest
}
>
{
children
}
<
/ButtonPrimary
>
}
}
...
...
src/components/CurrencyInputPanel/index.js
View file @
655b7956
...
@@ -48,10 +48,9 @@ const InputRow = styled.div`
...
@@ -48,10 +48,9 @@ const InputRow = styled.div`
const
CurrencySelect
=
styled
.
button
`
const
CurrencySelect
=
styled
.
button
`
align-items: center;
align-items: center;
height: 2.2rem;
height: 2.2rem;
font-size: 20px;
font-size: 20px;
background-color:
${({
selected
,
theme
})
=>
(
selected
?
theme
.
buttonBackgroundPlain
:
theme
.
zumthor
Blue
)}
;
background-color:
${({
selected
,
theme
})
=>
(
selected
?
theme
.
buttonBackgroundPlain
:
theme
.
royal
Blue
)}
;
color:
${({
selected
,
theme
})
=>
(
selected
?
theme
.
textColor
:
theme
.
royalBlu
e
)}
;
color:
${({
selected
,
theme
})
=>
(
selected
?
theme
.
textColor
:
theme
.
whit
e
)}
;
border: 1px solid
border: 1px solid
${({
selected
,
theme
,
disableTokenSelect
})
=>
${({
selected
,
theme
,
disableTokenSelect
})
=>
disableTokenSelect
?
theme
.
buttonBackgroundPlain
:
selected
?
theme
.
buttonOutlinePlain
:
theme
.
royalBlue
}
;
disableTokenSelect
?
theme
.
buttonBackgroundPlain
:
selected
?
theme
.
buttonOutlinePlain
:
theme
.
royalBlue
}
;
...
@@ -70,7 +69,8 @@ const CurrencySelect = styled.button`
...
@@ -70,7 +69,8 @@ const CurrencySelect = styled.button`
}
}
:active {
:active {
background-color:
${({
theme
})
=>
theme
.
zumthorBlue
}
;
background-color:
${({
selected
,
theme
})
=>
selected
?
darken
(
0.1
,
theme
.
zumthorBlue
)
:
darken
(
0.1
,
theme
.
royalBlue
)}
;
}
}
`
`
...
@@ -85,20 +85,20 @@ const StyledDropDown = styled(DropDown)`
...
@@ -85,20 +85,20 @@ const StyledDropDown = styled(DropDown)`
height: 35%;
height: 35%;
path {
path {
stroke:
${({
selected
,
theme
})
=>
(
selected
?
theme
.
textColor
:
theme
.
royalBlu
e
)}
;
stroke:
${({
selected
,
theme
})
=>
(
selected
?
theme
.
textColor
:
theme
.
whit
e
)}
;
}
}
`
`
const
InputPanel
=
styled
.
div
`
const
InputPanel
=
styled
.
div
`
${({
theme
})
=>
theme
.
flexColumnNoWrap
}
${({
theme
})
=>
theme
.
flexColumnNoWrap
}
position: relative;
position: relative;
border-radius:
1.25rem
;
border-radius:
${({
hideInput
})
=>
(
hideInput
?
'
8px
'
:
'
20px
'
)}
;
background-color:
${({
theme
})
=>
theme
.
inputBackground
}
;
background-color:
${({
theme
})
=>
theme
.
inputBackground
}
;
z-index: 1;
z-index: 1;
`
`
const
Container
=
styled
.
div
`
const
Container
=
styled
.
div
`
border-radius:
1.25rem
;
border-radius:
${({
hideInput
})
=>
(
hideInput
?
'
8px
'
:
'
20px
'
)}
;
border: 1px solid
${({
error
,
theme
})
=>
(
error
?
theme
.
salmonRed
:
theme
.
mercuryGray
)}
;
border: 1px solid
${({
error
,
theme
})
=>
(
error
?
theme
.
salmonRed
:
theme
.
mercuryGray
)}
;
background-color:
${({
theme
})
=>
theme
.
inputBackground
}
;
background-color:
${({
theme
})
=>
theme
.
inputBackground
}
;
...
@@ -174,7 +174,10 @@ export default function CurrencyInputPanel({
...
@@ -174,7 +174,10 @@ export default function CurrencyInputPanel({
hideBalance
=
false
,
hideBalance
=
false
,
isExchange
=
false
,
isExchange
=
false
,
exchange
=
null
,
// used for double token logo
exchange
=
null
,
// used for double token logo
customBalance
=
null
// used for LP balances instead of token balance
customBalance
=
null
,
// used for LP balances instead of token balance
hideInput
=
false
,
showSendWithSwap
=
false
,
onTokenSelectSendWithSwap
=
null
})
{
})
{
const
{
account
,
chainId
}
=
useWeb3React
()
const
{
account
,
chainId
}
=
useWeb3React
()
const
{
t
}
=
useTranslation
()
const
{
t
}
=
useTranslation
()
...
@@ -236,7 +239,7 @@ export default function CurrencyInputPanel({
...
@@ -236,7 +239,7 @@ export default function CurrencyInputPanel({
return
(
return
(
<
InputPanel
>
<
InputPanel
>
<
Container
error
=
{
!!
error
}
>
<
Container
error
=
{
!!
error
}
hideInput
=
{
hideInput
}
>
{
!
hideBalance
&&
(
{
!
hideBalance
&&
(
<
LabelRow
>
<
LabelRow
>
<
RowBetween
>
<
RowBetween
>
...
@@ -250,15 +253,19 @@ export default function CurrencyInputPanel({
...
@@ -250,15 +253,19 @@ export default function CurrencyInputPanel({
<
/RowBetween
>
<
/RowBetween
>
<
/LabelRow
>
<
/LabelRow
>
)}
)}
<
InputRow
>
<
InputRow
style
=
{
hideInput
?
{
padding
:
'
0
'
,
borderRadius
:
'
8px
'
}
:
{}}
hideInput
=
{
hideInput
}
>
<
NumericalInput
{
!
hideInput
&&
(
value
=
{
value
}
<>
onUserInput
=
{
val
=>
{
<
NumericalInput
onUserInput
(
field
,
val
)
value
=
{
value
}
}}
onUserInput
=
{
val
=>
{
/
>
onUserInput
(
field
,
val
)
{
!!
token
?.
address
&&
!
atMax
&&
<
StyledBalanceMax
onClick
=
{
onMax
}
>
MAX
<
/StyledBalanceMax>
}
}}
{
renderUnlockButton
()}
/
>
{
!!
token
?.
address
&&
!
atMax
&&
<
StyledBalanceMax
onClick
=
{
onMax
}
>
MAX
<
/StyledBalanceMax>
}
{
renderUnlockButton
()}
<
/
>
)}
<
CurrencySelect
<
CurrencySelect
selected
=
{
!!
token
?.
address
}
selected
=
{
!!
token
?.
address
}
onClick
=
{()
=>
{
onClick
=
{()
=>
{
...
@@ -296,6 +303,8 @@ export default function CurrencyInputPanel({
...
@@ -296,6 +303,8 @@ export default function CurrencyInputPanel({
urlAddedTokens
=
{
urlAddedTokens
}
urlAddedTokens
=
{
urlAddedTokens
}
field
=
{
field
}
field
=
{
field
}
onTokenSelect
=
{
onTokenSelection
}
onTokenSelect
=
{
onTokenSelection
}
showSendWithSwap
=
{
showSendWithSwap
}
onTokenSelectSendWithSwap
=
{
onTokenSelectSendWithSwap
}
/
>
/
>
)}
)}
<
/InputPanel
>
<
/InputPanel
>
...
...
src/components/ExchangePage/index.tsx
View file @
655b7956
...
@@ -4,6 +4,7 @@ import { ethers } from 'ethers'
...
@@ -4,6 +4,7 @@ import { ethers } from 'ethers'
import
{
parseUnits
,
parseEther
}
from
'
@ethersproject/units
'
import
{
parseUnits
,
parseEther
}
from
'
@ethersproject/units
'
import
{
WETH
,
TradeType
,
Route
,
Trade
,
TokenAmount
,
JSBI
}
from
'
@uniswap/sdk
'
import
{
WETH
,
TradeType
,
Route
,
Trade
,
TokenAmount
,
JSBI
}
from
'
@uniswap/sdk
'
import
QR
from
'
../../assets/svg/QR.svg
'
import
TokenLogo
from
'
../TokenLogo
'
import
TokenLogo
from
'
../TokenLogo
'
import
QuestionHelper
from
'
../Question
'
import
QuestionHelper
from
'
../Question
'
import
NumericalInput
from
'
../NumericalInput
'
import
NumericalInput
from
'
../NumericalInput
'
...
@@ -11,23 +12,23 @@ import ConfirmationModal from '../ConfirmationModal'
...
@@ -11,23 +12,23 @@ import ConfirmationModal from '../ConfirmationModal'
import
CurrencyInputPanel
from
'
../CurrencyInputPanel
'
import
CurrencyInputPanel
from
'
../CurrencyInputPanel
'
import
{
Link
}
from
'
../../theme/components
'
import
{
Link
}
from
'
../../theme/components
'
import
{
Text
}
from
'
rebass
'
import
{
Text
}
from
'
rebass
'
import
ThemeProvider
,
{
TYPE
}
from
'
../../theme
'
import
{
TYPE
}
from
'
../../theme
'
import
{
GreyCard
}
from
'
../../components/Card
'
import
{
GreyCard
,
LightCard
}
from
'
../../components/Card
'
import
{
ArrowDown
,
ArrowUp
}
from
'
react-feather
'
import
{
ArrowDown
,
ArrowUp
}
from
'
react-feather
'
import
{
ButtonPrimary
,
ButtonError
,
ButtonRadio
}
from
'
../Button
'
import
{
AutoColumn
,
ColumnCenter
}
from
'
../../components/Column
'
import
{
AutoColumn
,
ColumnCenter
}
from
'
../../components/Column
'
import
{
ButtonError
,
ButtonRadio
}
from
'
../Button
'
import
Row
,
{
RowBetween
,
RowFixed
}
from
'
../../components/Row
'
import
Row
,
{
RowBetween
,
RowFixed
}
from
'
../../components/Row
'
import
{
usePopups
}
from
'
../../contexts/Application
'
import
{
usePopups
}
from
'
../../contexts/Application
'
import
{
useToken
}
from
'
../../contexts/Tokens
'
import
{
useToken
}
from
'
../../contexts/Tokens
'
import
{
useExchange
}
from
'
../../contexts/Exchanges
'
import
{
useExchange
}
from
'
../../contexts/Exchanges
'
import
{
useWeb3React
}
from
'
../../hooks
'
import
{
useWeb3React
,
useTokenContract
}
from
'
../../hooks
'
import
{
useAddressBalance
}
from
'
../../contexts/Balances
'
import
{
useAddressBalance
}
from
'
../../contexts/Balances
'
import
{
useTransactionAdder
}
from
'
../../contexts/Transactions
'
import
{
useTransactionAdder
}
from
'
../../contexts/Transactions
'
import
{
useAddressAllowance
}
from
'
../../contexts/Allowances
'
import
{
useAddressAllowance
}
from
'
../../contexts/Allowances
'
import
{
ROUTER_ADDRESSES
}
from
'
../../constants
'
import
{
ROUTER_ADDRESSES
}
from
'
../../constants
'
import
{
getRouterContract
,
calculateGasMargin
}
from
'
../../utils
'
import
{
getRouterContract
,
calculateGasMargin
,
isAddress
,
getProviderOrSigner
}
from
'
../../utils
'
const
Wrapper
=
styled
.
div
`
const
Wrapper
=
styled
.
div
`
position: relative;
position: relative;
...
@@ -64,7 +65,66 @@ const InputWrapper = styled(RowBetween)`
...
@@ -64,7 +65,66 @@ const InputWrapper = styled(RowBetween)`
border-radius: 8px;
border-radius: 8px;
padding: 4px 8px;
padding: 4px 8px;
border: 1px solid transparent;
border: 1px solid transparent;
border:
${({
active
,
theme
})
=>
active
&&
'
1px solid
'
+
theme
.
royalBlue
}
;
border:
${({
active
,
error
,
theme
})
=>
error
?
'
1px solid
'
+
theme
.
salmonRed
:
active
?
'
1px solid
'
+
theme
.
royalBlue
:
''
}
;
`
const
InputGroup
=
styled
(
AutoColumn
)
`
position: relative;
padding: 40px 0;
`
const
QRWrapper
=
styled
.
div
`
display: flex;
align-items: center;
justify-content: center;
border: 1px solid
${({
theme
})
=>
theme
.
outlineGrey
}
;
background: #fbfbfb;
padding: 4px;
border-radius: 8px;
`
const
StyledInput
=
styled
.
input
`
width:
${({
width
})
=>
width
}
;
border: none;
outline: none;
font-size: 20px;
::placeholder {
color: #edeef2;
}
`
const
StyledNumerical
=
styled
(
NumericalInput
)
`
text-align: center;
font-size: 48px;
font-weight: 500px;
width: 100%;
::placeholder {
color: #edeef2;
}
`
const
MaxButton
=
styled
.
button
`
position: absolute;
right: 70px;
padding: 0.5rem 1rem;
background-color:
${({
theme
})
=>
theme
.
zumthorBlue
}
;
border: 1px solid
${({
theme
})
=>
theme
.
zumthorBlue
}
;
border-radius: 0.5rem;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
margin-right: 0.5rem;
color:
${({
theme
})
=>
theme
.
royalBlue
}
;
:hover {
border: 1px solid
${({
theme
})
=>
theme
.
royalBlue
}
;
}
:focus {
border: 1px solid
${({
theme
})
=>
theme
.
royalBlue
}
;
outline: none;
}
`
`
enum
Field
{
enum
Field
{
...
@@ -91,7 +151,7 @@ function initializeSwapState(inputAddress?: string, outputAddress?: string): Swa
...
@@ -91,7 +151,7 @@ function initializeSwapState(inputAddress?: string, outputAddress?: string): Swa
address
:
inputAddress
address
:
inputAddress
},
},
[
Field
.
OUTPUT
]:
{
[
Field
.
OUTPUT
]:
{
address
:
'
0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735
'
address
:
outputAddress
}
}
}
}
}
}
...
@@ -192,22 +252,28 @@ const INITIAL_ALLOWED_SLIPPAGE = 200
...
@@ -192,22 +252,28 @@ const INITIAL_ALLOWED_SLIPPAGE = 200
const
DEFAULT_DEADLINE_FROM_NOW
=
60
*
15
const
DEFAULT_DEADLINE_FROM_NOW
=
60
*
15
// used for warning states based on slippage in bips
// used for warning states based on slippage in bips
const
ALLOWED_
IMPACT
_MEDIUM
=
100
const
ALLOWED_
SLIPPAGE
_MEDIUM
=
100
const
ALLOWED_
IMPACT
_HIGH
=
500
const
ALLOWED_
SLIPPAGE
_HIGH
=
500
export
default
function
ExchangePage
()
{
export
default
function
ExchangePage
(
{
sendingInput
=
false
}
)
{
const
{
chainId
,
account
,
library
}
=
useWeb3React
()
const
{
chainId
,
account
,
library
}
=
useWeb3React
()
const
routerAddress
=
ROUTER_ADDRESSES
[
chainId
]
const
routerAddress
=
ROUTER_ADDRESSES
[
chainId
]
// adding notifications on txns
// adding notifications on txns
const
[,
addPopup
]
=
usePopups
()
const
[,
addPopup
]
=
usePopups
()
const
addTransaction
=
useTransactionAdder
()
// sending state
const
[
sending
,
setSending
]
=
useState
(
sendingInput
)
const
[
sendingWithSwap
,
setSendingWithSwap
]
=
useState
(
false
)
const
[
recipient
,
setRecipient
]
=
useState
(
''
)
// input details
// input details
const
[
state
,
dispatch
]
=
useReducer
(
reducer
,
WETH
[
chainId
].
address
,
initializeSwapState
)
const
[
state
,
dispatch
]
=
useReducer
(
reducer
,
WETH
[
chainId
].
address
,
initializeSwapState
)
const
{
independentField
,
typedValue
,
...
fieldData
}
=
state
const
{
independentField
,
typedValue
,
...
fieldData
}
=
state
const
dependentField
=
independentField
===
Field
.
INPUT
?
Field
.
OUTPUT
:
Field
.
INPUT
const
dependentField
=
independentField
===
Field
.
INPUT
?
Field
.
OUTPUT
:
Field
.
INPUT
const
tradeType
=
independentField
===
Field
.
INPUT
?
TradeType
.
EXACT_INPUT
:
TradeType
.
EXACT_OUTPUT
const
tradeType
=
independentField
===
Field
.
INPUT
?
TradeType
.
EXACT_INPUT
:
TradeType
.
EXACT_OUTPUT
const
[
tradeError
,
setTradeError
]
=
useState
(
''
)
// error for thin
sg liek reserve sizes
const
[
tradeError
,
setTradeError
]
=
useState
(
''
)
// error for thin
gs like reserve size or route
const
tokens
=
{
const
tokens
=
{
[
Field
.
INPUT
]:
useToken
(
fieldData
[
Field
.
INPUT
].
address
),
[
Field
.
INPUT
]:
useToken
(
fieldData
[
Field
.
INPUT
].
address
),
...
@@ -217,12 +283,18 @@ export default function ExchangePage() {
...
@@ -217,12 +283,18 @@ export default function ExchangePage() {
const
exchange
=
useExchange
(
tokens
[
Field
.
INPUT
],
tokens
[
Field
.
OUTPUT
])
const
exchange
=
useExchange
(
tokens
[
Field
.
INPUT
],
tokens
[
Field
.
OUTPUT
])
const
route
=
!!
exchange
?
new
Route
([
exchange
],
tokens
[
Field
.
INPUT
])
:
undefined
const
route
=
!!
exchange
?
new
Route
([
exchange
],
tokens
[
Field
.
INPUT
])
:
undefined
// modal state
// modal and loading
const
addTransaction
=
useTransactionAdder
()
const
[
showConfirm
,
setShowConfirm
]
=
useState
(
false
)
const
[
showConfirm
,
setShowConfirm
]
=
useState
(
true
)
const
[
pendingConfirmation
,
setPendingConfirmation
]
=
useState
(
true
)
// waiting for user confirmation
const
[
pendingConfirmation
,
setPendingConfirmation
]
=
useState
(
true
)
// waiting for user confirmation
const
[
attemptingTxn
,
setAttemptingTxn
]
=
useState
(
false
)
// clicked confirmed
const
[
attemptingTxn
,
setAttemptingTxn
]
=
useState
(
false
)
// clicked confirmed
// advanced settings
const
[
showAdvanced
,
setShowAdvanced
]
=
useState
(
false
)
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
)
// txn values
// txn values
const
[
txHash
,
setTxHash
]
=
useState
()
const
[
txHash
,
setTxHash
]
=
useState
()
const
[
deadline
,
setDeadline
]
=
useState
(
DEFAULT_DEADLINE_FROM_NOW
)
const
[
deadline
,
setDeadline
]
=
useState
(
DEFAULT_DEADLINE_FROM_NOW
)
...
@@ -240,10 +312,9 @@ export default function ExchangePage() {
...
@@ -240,10 +312,9 @@ export default function ExchangePage() {
const
parsedAmounts
:
{
[
field
:
number
]:
TokenAmount
}
=
{}
const
parsedAmounts
:
{
[
field
:
number
]:
TokenAmount
}
=
{}
// try to parse typed value
// try to parse typed value
// if (typedValue !== '' && typedValue !== '.' && tokens[independentField]) {
if
(
typedValue
!==
''
&&
typedValue
!==
'
.
'
&&
tokens
[
independentField
])
{
if
(
tokens
[
independentField
])
{
try
{
try
{
const
typedValueParsed
=
parseUnits
(
'
0.0001
'
,
tokens
[
independentField
].
decimals
).
toString
()
const
typedValueParsed
=
parseUnits
(
typedValue
,
tokens
[
independentField
].
decimals
).
toString
()
if
(
typedValueParsed
!==
'
0
'
)
if
(
typedValueParsed
!==
'
0
'
)
parsedAmounts
[
independentField
]
=
new
TokenAmount
(
tokens
[
independentField
],
typedValueParsed
)
parsedAmounts
[
independentField
]
=
new
TokenAmount
(
tokens
[
independentField
],
typedValueParsed
)
}
catch
(
error
)
{
}
catch
(
error
)
{
...
@@ -313,7 +384,7 @@ export default function ExchangePage() {
...
@@ -313,7 +384,7 @@ export default function ExchangePage() {
})
})
},
[])
},
[])
const
MIN_ETHER
=
new
TokenAmount
(
WETH
[
chainId
],
JSBI
.
BigInt
(
parseEther
(
'
.01
'
)))
const
MIN_ETHER
=
chainId
&&
new
TokenAmount
(
WETH
[
chainId
],
JSBI
.
BigInt
(
parseEther
(
'
.01
'
)))
const
maxAmountInput
=
const
maxAmountInput
=
!!
userBalances
[
Field
.
INPUT
]
&&
!!
userBalances
[
Field
.
INPUT
]
&&
JSBI
.
greaterThan
(
JSBI
.
greaterThan
(
...
@@ -388,16 +459,14 @@ export default function ExchangePage() {
...
@@ -388,16 +459,14 @@ export default function ExchangePage() {
outputApproval
&&
outputApproval
&&
JSBI
.
greaterThan
(
parsedAmounts
[
Field
.
OUTPUT
].
raw
,
outputApproval
.
raw
)
JSBI
.
greaterThan
(
parsedAmounts
[
Field
.
OUTPUT
].
raw
,
outputApproval
.
raw
)
// modal state
// parse the input for custom slippage
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
)
{
function
parseCustomInput
(
val
)
{
const
acceptableValues
=
[
/^$/
,
/^
\d{1,2}
$/
,
/^
\d{0,2}\.\d{0,2}
$/
]
const
acceptableValues
=
[
/^$/
,
/^
\d{1,2}
$/
,
/^
\d{0,2}\.\d{0,2}
$/
]
if
(
val
>
5
)
{
setSlippageInputError
(
'
Your transaction may be front-run.
'
)
}
else
{
setSlippageInputError
(
null
)
}
if
(
acceptableValues
.
some
(
a
=>
a
.
test
(
val
)))
{
if
(
acceptableValues
.
some
(
a
=>
a
.
test
(
val
)))
{
setCustomSlippage
(
val
)
setCustomSlippage
(
val
)
setAllowedSlippage
(
val
*
100
)
setAllowedSlippage
(
val
*
100
)
...
@@ -412,6 +481,62 @@ export default function ExchangePage() {
...
@@ -412,6 +481,62 @@ export default function ExchangePage() {
}
}
}
}
const
tokenContract
=
useTokenContract
(
tokens
[
Field
.
INPUT
]?.
address
)
// function for a pure send
async
function
onSend
()
{
setAttemptingTxn
(
true
)
const
signer
=
await
getProviderOrSigner
(
library
,
account
)
// get token contract if needed
let
estimate
:
Function
,
method
:
Function
,
args
,
value
if
(
tokens
[
Field
.
INPUT
]
===
WETH
[
chainId
])
{
signer
.
sendTransaction
({
to
:
recipient
.
toString
(),
value
:
hex
(
parsedAmounts
[
Field
.
INPUT
].
raw
)
})
.
then
(
response
=>
{
console
.
log
(
response
)
setTxHash
(
response
.
hash
)
addTransaction
(
response
)
setPendingConfirmation
(
false
)
})
.
catch
(
e
=>
{
addPopup
(
<
AutoColumn
gap=
"10px"
>
<
Text
>
Transaction Failed: try again.
</
Text
>
</
AutoColumn
>
)
resetModal
()
setShowConfirm
(
false
)
})
}
else
{
estimate
=
tokenContract
.
estimate
.
transfer
method
=
tokenContract
.
transfer
args
=
[
recipient
,
parsedAmounts
[
Field
.
INPUT
].
raw
.
toString
()]
value
=
ethers
.
constants
.
Zero
const
estimatedGasLimit
=
await
estimate
(...
args
,
{
value
}).
catch
(
e
=>
{
console
.
log
(
'
error getting gas limit
'
)
})
method
(...
args
,
{
value
,
gasLimit
:
calculateGasMargin
(
estimatedGasLimit
,
GAS_MARGIN
)
})
.
then
(
response
=>
{
setTxHash
(
response
.
hash
)
addTransaction
(
response
)
setPendingConfirmation
(
false
)
})
.
catch
(
e
=>
{
addPopup
(
<
AutoColumn
gap=
"10px"
>
<
Text
>
Transaction Failed: try again.
</
Text
>
</
AutoColumn
>
)
resetModal
()
setShowConfirm
(
false
)
})
}
}
async
function
onSwap
()
{
async
function
onSwap
()
{
const
routerContract
=
getRouterContract
(
chainId
,
library
,
account
)
const
routerContract
=
getRouterContract
(
chainId
,
library
,
account
)
setAttemptingTxn
(
true
)
setAttemptingTxn
(
true
)
...
@@ -497,7 +622,7 @@ export default function ExchangePage() {
...
@@ -497,7 +622,7 @@ export default function ExchangePage() {
gasLimit
:
calculateGasMargin
(
estimatedGasLimit
,
GAS_MARGIN
)
gasLimit
:
calculateGasMargin
(
estimatedGasLimit
,
GAS_MARGIN
)
})
})
.
then
(
response
=>
{
.
then
(
response
=>
{
setTxHash
(
response
)
setTxHash
(
response
.
hash
)
addTransaction
(
response
)
addTransaction
(
response
)
setPendingConfirmation
(
false
)
setPendingConfirmation
(
false
)
})
})
...
@@ -513,17 +638,38 @@ export default function ExchangePage() {
...
@@ -513,17 +638,38 @@ export default function ExchangePage() {
}
}
// errors
// errors
const
[
generalError
,
setGeneralError
]
=
useState
(
''
)
const
[
inputError
,
setInputError
]
=
useState
(
''
)
const
[
inputError
,
setInputError
]
=
useState
(
''
)
const
[
outputError
,
setOutputError
]
=
useState
(
''
)
const
[
outputError
,
setOutputError
]
=
useState
(
''
)
const
[
recipientError
,
setRecipientError
]
=
useState
(
''
)
const
[
isValid
,
setIsValid
]
=
useState
(
false
)
const
[
isValid
,
setIsValid
]
=
useState
(
false
)
const
ignoreOutput
=
sending
?
!
sendingWithSwap
:
false
useEffect
(()
=>
{
useEffect
(()
=>
{
// reset errors
// reset errors
setGeneralError
(
null
)
setInputError
(
null
)
setInputError
(
null
)
setOutputError
(
null
)
setOutputError
(
null
)
setTradeError
(
null
)
setTradeError
(
null
)
setRecipientError
(
null
)
setIsValid
(
true
)
setIsValid
(
true
)
if
(
!
isAddress
(
recipient
)
&&
sending
)
{
setRecipientError
(
'
Invalid Recipient
'
)
setIsValid
(
false
)
}
if
(
!
parsedAmounts
[
Field
.
INPUT
])
{
setGeneralError
(
'
Enter an amount
'
)
setIsValid
(
false
)
}
if
(
!
parsedAmounts
[
Field
.
OUTPUT
]
&&
!
ignoreOutput
)
{
setGeneralError
(
'
Enter an amount
'
)
setIsValid
(
false
)
}
if
(
if
(
parsedAmounts
[
Field
.
INPUT
]
&&
parsedAmounts
[
Field
.
INPUT
]
&&
exchange
&&
exchange
&&
...
@@ -534,6 +680,7 @@ export default function ExchangePage() {
...
@@ -534,6 +680,7 @@ export default function ExchangePage() {
}
}
if
(
if
(
!
ignoreOutput
&&
parsedAmounts
[
Field
.
OUTPUT
]
&&
parsedAmounts
[
Field
.
OUTPUT
]
&&
exchange
&&
exchange
&&
JSBI
.
greaterThan
(
parsedAmounts
[
Field
.
OUTPUT
].
raw
,
exchange
.
reserveOf
(
tokens
[
Field
.
OUTPUT
]).
raw
)
JSBI
.
greaterThan
(
parsedAmounts
[
Field
.
OUTPUT
].
raw
,
exchange
.
reserveOf
(
tokens
[
Field
.
OUTPUT
]).
raw
)
...
@@ -542,12 +689,12 @@ export default function ExchangePage() {
...
@@ -542,12 +689,12 @@ export default function ExchangePage() {
setIsValid
(
false
)
setIsValid
(
false
)
}
}
if
(
showInputUnlock
)
{
if
(
showInputUnlock
&&
!
(
sending
&&
!
sendingWithSwap
)
)
{
setInputError
(
'
Approval Needed
'
)
setInputError
(
'
Approval Needed
'
)
setIsValid
(
false
)
setIsValid
(
false
)
}
}
if
(
showOutputUnlock
)
{
if
(
showOutputUnlock
&&
!
ignoreOutput
)
{
setOutputError
(
'
Approval Needed
'
)
setOutputError
(
'
Approval Needed
'
)
setIsValid
(
false
)
setIsValid
(
false
)
}
}
...
@@ -560,19 +707,22 @@ export default function ExchangePage() {
...
@@ -560,19 +707,22 @@ export default function ExchangePage() {
setInputError
(
'
Insufficient balance.
'
)
setInputError
(
'
Insufficient balance.
'
)
setIsValid
(
false
)
setIsValid
(
false
)
}
}
},
[
if
(
exchange
,
userBalances
[
Field
.
OUTPUT
]
&&
ignoreOutput
,
parsedAmounts
[
Field
.
OUTPUT
]
&&
parsedAmounts
,
JSBI
.
lessThan
(
userBalances
[
Field
.
OUTPUT
].
raw
,
parsedAmounts
[
Field
.
OUTPUT
]?.
raw
)
recipient
,
)
{
sending
,
setOutputError
(
'
Insufficient balance.
'
)
sendingWithSwap
,
setIsValid
(
false
)
showInputUnlock
,
}
showOutputUnlock
,
},
[
exchange
,
parsedAmounts
,
showInputUnlock
,
showOutputUnlock
,
tokens
,
userBalances
])
tokens
,
userBalances
const
warningMedium
=
slippageFromTrade
&&
parseFloat
(
slippageFromTrade
.
toFixed
(
4
))
>
ALLOWED_IMPACT_MEDIUM
/
100
])
const
warningHigh
=
slippageFromTrade
&&
parseFloat
(
slippageFromTrade
.
toFixed
(
4
))
>
ALLOWED_IMPACT_HIGH
/
100
// warnings on slippage
const
warningMedium
=
slippageFromTrade
&&
parseFloat
(
slippageFromTrade
.
toFixed
(
4
))
>
ALLOWED_SLIPPAGE_MEDIUM
/
100
const
warningHigh
=
slippageFromTrade
&&
parseFloat
(
slippageFromTrade
.
toFixed
(
4
))
>
ALLOWED_SLIPPAGE_HIGH
/
100
function
resetModal
()
{
function
resetModal
()
{
setPendingConfirmation
(
true
)
setPendingConfirmation
(
true
)
...
@@ -581,169 +731,221 @@ export default function ExchangePage() {
...
@@ -581,169 +731,221 @@ export default function ExchangePage() {
}
}
function
modalHeader
()
{
function
modalHeader
()
{
return
(
if
(
sending
&&
!
sendingWithSwap
)
{
<
AutoColumn
gap=
{
'
20px
'
}
style=
{
{
marginTop
:
'
40px
'
}
}
>
return
(
<
RowBetween
align=
"flex-end"
>
<
AutoColumn
gap=
"30px"
style=
{
{
marginTop
:
'
40px
'
}
}
>
<
Text
fontSize=
{
36
}
fontWeight=
{
500
}
>
<
RowBetween
>
{
!!
slippageAdjustedAmounts
[
Field
.
INPUT
]
&&
slippageAdjustedAmounts
[
Field
.
INPUT
].
toSignificant
(
6
)
}
<
Text
fontSize=
{
36
}
fontWeight=
{
500
}
>
</
Text
>
{
parsedAmounts
[
Field
.
INPUT
]?.
toFixed
(
8
)
}
<
RowFixed
gap=
"10px"
>
<
TokenLogo
address=
{
tokens
[
Field
.
INPUT
]?.
address
}
size=
{
'
24px
'
}
/>
<
Text
fontSize=
{
24
}
fontWeight=
{
500
}
style=
{
{
marginLeft
:
'
10px
'
}
}
>
{
tokens
[
Field
.
INPUT
]?.
symbol
||
''
}
</
Text
>
</
Text
>
</
RowFixed
>
<
TokenLogo
address=
{
tokens
[
Field
.
INPUT
]?.
address
}
size=
{
'
30px
'
}
/>
</
RowBetween
>
</
RowBetween
>
<
RowFixed
>
<
ArrowDown
size=
{
24
}
color=
"#888D9B"
/>
<
ArrowDown
size=
"16"
color=
"#888D9B"
/>
<
TYPE
.
blue
fontSize=
{
36
}
>
</
RowFixed
>
{
recipient
?.
slice
(
0
,
6
)
}
...
{
recipient
?.
slice
(
36
,
42
)
}
<
RowBetween
align=
"flex-end"
>
</
TYPE
.
blue
>
<
Text
fontSize=
{
36
}
fontWeight=
{
500
}
color=
{
warningHigh
?
'
#FF6871
'
:
'
#2172E5
'
}
>
</
AutoColumn
>
{
!!
slippageAdjustedAmounts
[
Field
.
OUTPUT
]
&&
slippageAdjustedAmounts
[
Field
.
OUTPUT
].
toSignificant
(
6
)
}
)
</
Text
>
}
<
RowFixed
gap=
"10px"
>
<
TokenLogo
address=
{
tokens
[
Field
.
OUTPUT
]?.
address
}
size=
{
'
24px
'
}
/>
if
(
sending
&&
sendingWithSwap
)
{
<
Text
fontSize=
{
24
}
fontWeight=
{
500
}
style=
{
{
marginLeft
:
'
10px
'
}
}
>
}
{
tokens
[
Field
.
OUTPUT
]?.
symbol
||
''
}
if
(
!
sending
)
{
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
>
</
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
>
</
RowFixed
>
</
RowBetween
>
<
RowBetween
align=
"flex-end"
>
</
AutoColumn
>
<
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
>
)
}
}
}
function
modalBottom
()
{
function
modalBottom
()
{
return
showAdvanced
?
(
if
(
sending
&&
!
sendingWithSwap
)
{
<
AutoColumn
gap=
"20px"
>
return
(
<
Link
<
AutoColumn
>
onClick=
{
()
=>
{
<
ButtonPrimary
onClick=
{
onSend
}
>
setShowAdvanced
(
false
)
<
Text
color=
"white"
fontSize=
{
20
}
>
}
}
Confirm send
>
</
Text
>
back
</
ButtonPrimary
>
</
Link
>
</
AutoColumn
>
<
RowBetween
>
)
<
TYPE
.
main
>
Limit additional price slippage
</
TYPE
.
main
>
}
<
QuestionHelper
text=
""
/>
</
RowBetween
>
if
(
sending
&&
sendingWithSwap
)
{
<
Row
>
}
<
ButtonRadio
active=
{
SLIPPAGE_INDEX
[
1
]
===
activeIndex
}
if
(
showAdvanced
)
{
padding=
"4px 6px"
return
(
borderRadius=
"8px"
<
AutoColumn
gap=
"20px"
>
style=
{
{
marginRight
:
'
16px
'
}
}
<
Link
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=
{
()
=>
{
onClick=
{
()
=>
{
setActiveIndex
(
SLIPPAGE_INDEX
[
3
])
setShowAdvanced
(
false
)
setAllowedSlippage
(
200
)
}
}
}
}
>
>
2% (suggested)
back
</
ButtonRadio
>
</
Link
>
</
Row
>
<
RowBetween
>
<
RowFixed
>
<
TYPE
.
main
>
Limit additional price impact
</
TYPE
.
main
>
<
InputWrapper
active=
{
SLIPPAGE_INDEX
[
4
]
===
activeIndex
}
>
<
QuestionHelper
text=
""
/>
<
NumericalInput
</
RowBetween
>
align=
{
customSlippage
?
'
right
'
:
'
left
'
}
<
Row
>
value=
{
customSlippage
||
''
}
<
ButtonRadio
onUserInput=
{
val
=>
{
active=
{
SLIPPAGE_INDEX
[
1
]
===
activeIndex
}
parseCustomInput
(
val
)
padding=
"4px 6px"
setActiveIndex
(
SLIPPAGE_INDEX
[
4
])
borderRadius=
"8px"
style=
{
{
marginRight
:
'
16px
'
}
}
width=
{
'
60px
'
}
onClick=
{
()
=>
{
setActiveIndex
(
SLIPPAGE_INDEX
[
1
])
setAllowedSlippage
(
10
)
}
}
}
}
placeHolder=
"Custom"
>
0.1%
</
ButtonRadio
>
<
ButtonRadio
active=
{
SLIPPAGE_INDEX
[
2
]
===
activeIndex
}
padding=
"4px 6px"
borderRadius=
"8px"
style=
{
{
marginRight
:
'
16px
'
}
}
width=
{
'
60px
'
}
onClick=
{
()
=>
{
onClick=
{
()
=>
{
setActiveIndex
(
SLIPPAGE_INDEX
[
4
])
setActiveIndex
(
SLIPPAGE_INDEX
[
2
])
if
(
customSlippage
)
{
setAllowedSlippage
(
100
)
parseCustomInput
(
customSlippage
)
}
}
}
}
}
/
>
>
%
1
%
</
InputWrapper
>
</
ButtonRadio
>
</
RowFixed
>
<
ButtonRadio
<
RowBetween
>
active=
{
SLIPPAGE_INDEX
[
3
]
===
activeIndex
}
<
TYPE
.
main
>
Adjust deadline (minutes from now)
</
TYPE
.
main
>
padding=
"4px"
</
RowBetween
>
borderRadius=
"8px"
<
RowFixed
>
width=
{
'
140px
'
}
<
NumericalInput
onClick=
{
()
=>
{
value=
{
customDeadline
}
setActiveIndex
(
SLIPPAGE_INDEX
[
3
])
onUserInput=
{
val
=>
{
setAllowedSlippage
(
200
)
parseCustomDeadline
(
val
)
}
}
}
}
>
/>
2% (suggested)
</
RowFixed
>
</
ButtonRadio
>
</
AutoColumn
>
</
Row
>
)
:
(
<
RowFixed
>
<
>
<
InputWrapper
active=
{
SLIPPAGE_INDEX
[
4
]
===
activeIndex
}
error=
{
slippageInputError
}
>
<
RowBetween
>
<
NumericalInput
<
Text
color=
"#565A69"
fontWeight=
{
500
}
fontSize=
{
16
}
>
align=
{
customSlippage
?
'
right
'
:
'
left
'
}
Price
value=
{
customSlippage
||
''
}
</
Text
>
onUserInput=
{
val
=>
{
<
Text
fontWeight=
{
500
}
fontSize=
{
16
}
>
parseCustomInput
(
val
)
{
`1 ${tokens[Field.INPUT]?.symbol} = ${route && route.midPrice && route.midPrice.adjusted.toFixed(8)} ${
setActiveIndex
(
SLIPPAGE_INDEX
[
4
])
tokens[Field.OUTPUT]?.symbol
}
}
}`
}
placeHolder=
"Custom"
</
Text
>
onClick=
{
()
=>
{
</
RowBetween
>
setActiveIndex
(
SLIPPAGE_INDEX
[
4
])
<
RowBetween
>
if
(
customSlippage
)
{
<
Text
color=
"#565A69"
fontWeight=
{
500
}
fontSize=
{
16
}
>
parseCustomInput
(
customSlippage
)
Slippage
<
Link
onClick=
{
()
=>
setShowAdvanced
(
true
)
}
>
(edit limits)
</
Link
>
}
</
Text
>
}
}
<
ErrorText
warningHigh=
{
warningHigh
}
fontWeight=
{
500
}
>
/
>
{
slippageFromTrade
&&
slippageFromTrade
.
toFixed
(
4
)
}
%
%
</
ErrorText
>
</
InputWrapper
>
</
RowBetween
>
{
slippageInputError
&&
(
<
ButtonError
onClick=
{
onSwap
}
error=
{
!!
warningHigh
}
style=
{
{
margin
:
'
10px 0
'
}
}
>
<
TYPE
.
error
error=
{
true
}
fontSize=
{
12
}
style=
{
{
marginLeft
:
'
10px
'
}
}
>
<
Text
fontSize=
{
20
}
fontWeight=
{
500
}
>
Your transaction may be front-run
{
warningHigh
?
'
Swap Anyway
'
:
'
Swap
'
}
</
TYPE
.
error
>
</
Text
>
)
}
</
ButtonError
>
</
RowFixed
>
<
Text
fontSize=
{
12
}
color=
"#565A69"
textAlign=
"center"
>
<
RowBetween
>
{
`Output is estimated. You will receive at least ${slippageAdjustedAmounts[Field.OUTPUT]?.toSignificant(6)} ${
<
TYPE
.
main
>
Adjust deadline (minutes from now)
</
TYPE
.
main
>
tokens[Field.OUTPUT]?.symbol
</
RowBetween
>
} or the transaction will revert.`
}
<
RowFixed
>
</
Text
>
<
InputWrapper
>
<
AutoColumn
justify=
"center"
>
<
NumericalInput
<
Link
value=
{
customDeadline
}
onClick=
{
()
=>
{
onUserInput=
{
val
=>
{
setShowAdvanced
(
true
)
parseCustomDeadline
(
val
)
}
}
}
}
>
/
>
Advanced Options
</
InputWrapper
>
</
Link
>
</
RowFixed
>
</
AutoColumn
>
</
AutoColumn
>
</>
)
)
}
if
(
!
sending
)
{
return
(
<>
<
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
>
<
AutoColumn
justify=
"center"
gap=
"20px"
>
<
TYPE
.
italic
textAlign=
"center"
style=
{
{
width
:
'
80%
'
}
}
>
{
`Output is estimated. You will receive at least ${slippageAdjustedAmounts[Field.OUTPUT]?.toSignificant(
6
)} ${tokens[Field.OUTPUT]?.symbol} or the transaction will revert.`
}
</
TYPE
.
italic
>
<
Link
onClick=
{
()
=>
{
setShowAdvanced
(
true
)
}
}
>
Advanced Options
</
Link
>
</
AutoColumn
>
</>
)
}
}
}
const
pendingText
=
` Swapped
${
parsedAmounts
[
Field
.
INPUT
]?.
toSignificant
(
6
)}
$
{
const
pendingText
=
sending
tokens
[
Field
.
INPUT
]?.
symbol
?
`Sending
${
parsedAmounts
[
Field
.
INPUT
]?.
toSignificant
(
6
)}
$
{
tokens
[
Field
.
INPUT
]?.
symbol
}
to
$
{
recipient
}
`
}
for
$
{
parsedAmounts
[
Field
.
OUTPUT
]?.
toSignificant
(
6
)}
$
{
tokens
[
Field
.
OUTPUT
]?.
symbol
}
`
: `
Swapped
$
{
parsedAmounts
[
Field
.
INPUT
]?.
toSignificant
(
6
)}
$
{
tokens
[
Field
.
INPUT
]?.
symbol
}
for
$
{
parsedAmounts
[
Field
.
OUTPUT
]?.
toSignificant
(
6
)}
$
{
tokens
[
Field
.
OUTPUT
]?.
symbol
}
`
return (
return (
<Wrapper>
<Wrapper>
...
@@ -756,69 +958,128 @@ export default function ExchangePage() {
...
@@ -756,69 +958,128 @@ export default function ExchangePage() {
attemptingTxn={attemptingTxn}
attemptingTxn={attemptingTxn}
pendingConfirmation={pendingConfirmation}
pendingConfirmation={pendingConfirmation}
hash={txHash ? txHash : ''}
hash={txHash ? txHash : ''}
topContent={
() => modalHeader()
}
topContent={
modalHeader
}
bottomContent={modalBottom}
bottomContent={modalBottom}
pendingText={pendingText}
pendingText={pendingText}
title=
"Confirm Swap"
title=
{sendingWithSwap ? 'Confirm swap and send' : sending ? 'Confirm Send' : 'Confirm Swap'}
/>
/>
{sending && !sendingWithSwap && (
<>
<InputGroup gap="24px" justify="center">
{!atMaxAmountInput && (
<MaxButton
onClick={() => {
maxAmountInput && onMaxInput(maxAmountInput.toExact())
}}
>
Max
</MaxButton>
)}
<StyledNumerical value={formattedAmounts[Field.INPUT]} onUserInput={val => onUserInput(Field.INPUT, val)} />
{!parsedAmounts[Field.INPUT] && <TYPE.gray>Enter an amount.</TYPE.gray>}
<CurrencyInputPanel
field={Field.INPUT}
value={formattedAmounts[Field.INPUT]}
onUserInput={val => onUserInput(Field.INPUT, val)}
onMax={() => {
maxAmountInput && onMaxInput(maxAmountInput.toExact())
}}
atMax={atMaxAmountInput}
token={tokens[Field.INPUT]}
onTokenSelection={address => onTokenSelection(Field.INPUT, address)}
onTokenSelectSendWithSwap={address => {
onTokenSelection(Field.OUTPUT, address)
setSendingWithSwap(true)
}}
title={'Input'}
error={inputError}
exchange={exchange}
showUnlock={showInputUnlock}
hideBalance={true}
hideInput={true}
showSendWithSwap={true}
/>
</InputGroup>
</>
)}
<AutoColumn gap={'20px'}>
<AutoColumn gap={'20px'}>
<CurrencyInputPanel
{(!sending || sendingWithSwap) && (
field={Field.INPUT}
<>
value={formattedAmounts[Field.INPUT]}
<CurrencyInputPanel
onUserInput={onUserInput}
field={Field.INPUT}
onMax={() => {
value={formattedAmounts[Field.INPUT]}
maxAmountInput && onMaxInput(maxAmountInput.toExact())
onUserInput={onUserInput}
}}
onMax={() => {
atMax={atMaxAmountInput}
maxAmountInput && onMaxInput(maxAmountInput.toExact())
token={tokens[Field.INPUT]}
}}
onTokenSelection={address => onTokenSelection(Field.INPUT, address)}
atMax={atMaxAmountInput}
title={'Input'}
token={tokens[Field.INPUT]}
error={inputError}
onTokenSelection={address => onTokenSelection(Field.INPUT, address)}
exchange={exchange}
title={'Input'}
showUnlock={showInputUnlock}
error={inputError}
/>
exchange={exchange}
<ColumnCenter>
showUnlock={showInputUnlock}
<ArrowWrapper onClick={onSwapTokens}>
/>
<ArrowDown size="16" color="#2F80ED" />
<ColumnCenter>
<ArrowUp size="16" color="#2F80ED" />
<ArrowWrapper onClick={onSwapTokens}>
</ArrowWrapper>
<ArrowDown size="16" color="#2F80ED" />
</ColumnCenter>
<ArrowUp size="16" color="#2F80ED" />
<CurrencyInputPanel
</ArrowWrapper>
field={Field.OUTPUT}
</ColumnCenter>
value={formattedAmounts[Field.OUTPUT]}
<CurrencyInputPanel
onUserInput={onUserInput}
field={Field.OUTPUT}
onMax={() => {
value={formattedAmounts[Field.OUTPUT]}
maxAmountOutput && onMaxOutput(maxAmountOutput.toExact())
onUserInput={onUserInput}
}}
onMax={() => {
atMax={atMaxAmountOutput}
maxAmountOutput && onMaxOutput(maxAmountOutput.toExact())
token={tokens[Field.OUTPUT]}
}}
onTokenSelection={address => onTokenSelection(Field.OUTPUT, address)}
atMax={atMaxAmountOutput}
title={'Output'}
token={tokens[Field.OUTPUT]}
error={outputError}
onTokenSelection={address => onTokenSelection(Field.OUTPUT, address)}
exchange={exchange}
title={'Output'}
showUnlock={showOutputUnlock}
error={outputError}
/>
exchange={exchange}
<RowBetween>
showUnlock={showOutputUnlock}
<Text fontWeight={500} color="#565A69">
/>
Price
<RowBetween>
</Text>
<Text fontWeight={500} color="#565A69">
<Text fontWeight={500} color="#565A69">
Price
{exchange
</Text>
? `
1
$
{
tokens
[
Field
.
INPUT
].
symbol
}
=
$
{
route
?.
midPrice
.
toSignificant
(
6
)}
$
{
tokens
[
Field
.
OUTPUT
].
symbol
}
`
<Text fontWeight={500} color="#565A69">
: '-'}
{exchange
</Text>
? `
1
$
{
tokens
[
Field
.
INPUT
].
symbol
}
=
$
{
route
?.
midPrice
.
toSignificant
(
6
)}
$
{
</RowBetween>
tokens
[
Field
.
OUTPUT
].
symbol
{warningMedium && (
}
`
<RowBetween>
: '-'}
<Text fontWeight={500} color="#565A69">
</Text>
Slippage
</RowBetween>
</Text>
{warningMedium && (
<ErrorText fontWeight={500} warningMedium={warningMedium} warningHigh={warningHigh}>
<RowBetween>
{slippageFromTrade.toFixed(4)}%
<Text fontWeight={500} color="#565A69">
</ErrorText>
Slippage
</RowBetween>
</Text>
<ErrorText fontWeight={500} warningMedium={warningMedium} warningHigh={warningHigh}>
{slippageFromTrade.toFixed(4)}%
</ErrorText>
</RowBetween>
)}
</>
)}
)}
{sending && (
<AutoColumn gap="10px">
<LightCard borderRadius={'20px'}>
<RowBetween>
<StyledInput placeholder="Recipient Address" onChange={e => setRecipient(e.target.value)} />
<QRWrapper>
<img src={QR} alt="" />
</QRWrapper>
</RowBetween>
</LightCard>
</AutoColumn>
)}
<ButtonError
<ButtonError
onClick={() => {
onClick={() => {
setShowConfirm(true)
setShowConfirm(true)
...
@@ -827,10 +1088,14 @@ export default function ExchangePage() {
...
@@ -827,10 +1088,14 @@ export default function ExchangePage() {
error={!!warningHigh}
error={!!warningHigh}
>
>
<Text fontSize={20} fontWeight={500}>
<Text fontSize={20} fontWeight={500}>
{inputError
{generalError
? generalError
: inputError
? inputError
? inputError
: outputError
: outputError
? outputError
? outputError
: recipientError
? recipientError
: tradeError
: tradeError
? tradeError
? tradeError
: warningHigh
: warningHigh
...
@@ -839,6 +1104,7 @@ export default function ExchangePage() {
...
@@ -839,6 +1104,7 @@ export default function ExchangePage() {
</Text>
</Text>
</ButtonError>
</ButtonError>
</AutoColumn>
</AutoColumn>
{warningHigh && (
{warningHigh && (
<FixedBottom>
<FixedBottom>
<GreyCard>
<GreyCard>
...
...
src/components/NumericalInput/index.tsx
View file @
655b7956
...
@@ -5,11 +5,11 @@ const StyledInput = styled.input`
...
@@ -5,11 +5,11 @@ const StyledInput = styled.input`
color:
${({
error
,
theme
})
=>
error
&&
theme
.
salmonRed
}
;
color:
${({
error
,
theme
})
=>
error
&&
theme
.
salmonRed
}
;
background-color:
${({
theme
})
=>
theme
.
inputBackground
}
;
background-color:
${({
theme
})
=>
theme
.
inputBackground
}
;
color:
${({
theme
})
=>
theme
.
textColor
}
;
color:
${({
theme
})
=>
theme
.
textColor
}
;
width: 0;
font-size: 20px;
font-size: 20px;
outline: none;
outline: none;
border: none;
border: none;
flex: 1 1 auto;
flex: 1 1 auto;
width: 0;
background-color:
${({
theme
})
=>
theme
.
inputBackground
}
;
background-color:
${({
theme
})
=>
theme
.
inputBackground
}
;
font-size:
${({
fontSize
})
=>
fontSize
&&
fontSize
}
;
font-size:
${({
fontSize
})
=>
fontSize
&&
fontSize
}
;
text-align:
${({
align
})
=>
align
&&
align
}
;
text-align:
${({
align
})
=>
align
&&
align
}
;
...
...
src/components/SearchModal/index.js
View file @
655b7956
import
React
,
{
useState
,
useRef
,
useMemo
,
useEffect
}
from
'
react
'
import
React
,
{
useState
,
useRef
,
useMemo
,
useEffect
}
from
'
react
'
import
{
withRouter
}
from
'
react-router-dom
'
import
'
@reach/tooltip/styles.css
'
import
{
Link
}
from
'
react-router-dom
'
import
{
Link
as
StyledLink
}
from
'
../../theme/components
'
import
{
useTranslation
}
from
'
react-i18next
'
import
{
ethers
}
from
'
ethers
'
import
styled
from
'
styled-components
'
import
styled
from
'
styled-components
'
import
escapeStringRegex
from
'
escape-string-regexp
'
import
escapeStringRegex
from
'
escape-string-regexp
'
import
'
@reach/tooltip/styles.css
'
import
{
Link
}
from
'
react-router-dom
'
import
{
ethers
}
from
'
ethers
'
import
{
isMobile
}
from
'
react-device-detect
'
import
{
isMobile
}
from
'
react-device-detect
'
import
{
withRouter
}
from
'
react-router-dom
'
import
{
JSBI
}
from
'
@uniswap/sdk
'
import
{
Text
}
from
'
rebass
'
import
{
Link
as
StyledLink
}
from
'
../../theme/components
'
import
Column
,
{
AutoColumn
}
from
'
../Column
'
import
{
RowBetween
,
RowFixed
}
from
'
../Row
'
import
Modal
from
'
../Modal
'
import
Circle
from
'
../../assets/images/circle.svg
'
import
TokenLogo
from
'
../TokenLogo
'
import
TokenLogo
from
'
../TokenLogo
'
import
{
CloseIcon
}
from
'
../../theme/components
'
import
DoubleTokenLogo
from
'
../DoubleLogo
'
import
DoubleTokenLogo
from
'
../DoubleLogo
'
import
{
useWeb3React
}
from
'
../../hooks
'
import
Column
,
{
AutoColumn
}
from
'
../Column
'
import
{
isAddress
}
from
'
../../utils
'
import
{
Text
}
from
'
rebass
'
import
Modal
from
'
../Modal
'
import
{
useToken
,
useAllTokens
,
INITIAL_TOKENS_CONTEXT
}
from
'
../../contexts/Tokens
'
import
{
Spinner
}
from
'
../../theme
'
import
{
Spinner
}
from
'
../../theme
'
import
Circle
from
'
../../assets/images/circle.svg
'
import
{
CloseIcon
}
from
'
../../theme/components
'
import
{
ColumnCenter
}
from
'
../../components/Column
'
import
{
RowBetween
,
RowFixed
}
from
'
../Row
'
import
{
isAddress
}
from
'
../../utils
'
import
{
useWeb3React
}
from
'
../../hooks
'
import
{
useAllBalances
}
from
'
../../contexts/Balances
'
import
{
useAllBalances
}
from
'
../../contexts/Balances
'
import
{
useTranslation
}
from
'
react-i18next
'
import
{
useAllExchanges
}
from
'
../../contexts/Exchanges
'
import
{
useAllExchanges
}
from
'
../../contexts/Exchanges
'
import
{
useToken
,
useAllTokens
,
INITIAL_TOKENS_CONTEXT
}
from
'
../../contexts/Tokens
'
const
TokenModalInfo
=
styled
.
div
`
const
TokenModalInfo
=
styled
.
div
`
${({
theme
})
=>
theme
.
flexRowNoWrap
}
${({
theme
})
=>
theme
.
flexRowNoWrap
}
...
@@ -108,7 +112,17 @@ const MenuItem = styled(PaddedItem)`
...
@@ -108,7 +112,17 @@ const MenuItem = styled(PaddedItem)`
background-color:
${({
theme
})
=>
theme
.
tokenRowHover
}
;
background-color:
${({
theme
})
=>
theme
.
tokenRowHover
}
;
}
}
`
`
function
SearchModal
({
history
,
isOpen
,
onDismiss
,
onTokenSelect
,
urlAddedTokens
,
filterType
,
hiddenToken
})
{
function
SearchModal
({
history
,
isOpen
,
onDismiss
,
onTokenSelect
,
urlAddedTokens
,
filterType
,
hiddenToken
,
showSendWithSwap
,
onTokenSelectSendWithSwap
})
{
const
{
t
}
=
useTranslation
()
const
{
t
}
=
useTranslation
()
const
{
account
,
chainId
}
=
useWeb3React
()
const
{
account
,
chainId
}
=
useWeb3React
()
...
@@ -171,6 +185,7 @@ function SearchModal({ history, isOpen, onDismiss, onTokenSelect, urlAddedTokens
...
@@ -171,6 +185,7 @@ function SearchModal({ history, isOpen, onDismiss, onTokenSelect, urlAddedTokens
let
balance
let
balance
// only update if we have data
// only update if we have data
balance
=
allBalances
?.[
account
]?.[
k
]
balance
=
allBalances
?.[
account
]?.[
k
]
return
{
return
{
name
:
allTokens
[
k
].
name
,
name
:
allTokens
[
k
].
name
,
symbol
:
allTokens
[
k
].
symbol
,
symbol
:
allTokens
[
k
].
symbol
,
...
@@ -203,10 +218,16 @@ function SearchModal({ history, isOpen, onDismiss, onTokenSelect, urlAddedTokens
...
@@ -203,10 +218,16 @@ function SearchModal({ history, isOpen, onDismiss, onTokenSelect, urlAddedTokens
})
})
},
[
tokenList
,
searchQuery
])
},
[
tokenList
,
searchQuery
])
function
_onTokenSelect
(
address
)
{
function
_onTokenSelect
(
address
,
sendWithSwap
=
false
)
{
setSearchQuery
(
''
)
if
(
sendWithSwap
)
{
onTokenSelect
(
address
)
setSearchQuery
(
''
)
onDismiss
()
onTokenSelectSendWithSwap
(
address
)
onDismiss
()
}
else
{
setSearchQuery
(
''
)
onTokenSelect
(
address
)
onDismiss
()
}
}
}
// manage focus on modal show
// manage focus on modal show
...
@@ -340,8 +361,11 @@ function SearchModal({ history, isOpen, onDismiss, onTokenSelect, urlAddedTokens
...
@@ -340,8 +361,11 @@ function SearchModal({ history, isOpen, onDismiss, onTokenSelect, urlAddedTokens
INITIAL_TOKENS_CONTEXT
[
chainId
]
&&
INITIAL_TOKENS_CONTEXT
[
chainId
]
&&
!
INITIAL_TOKENS_CONTEXT
[
chainId
].
hasOwnProperty
(
address
)
&&
!
INITIAL_TOKENS_CONTEXT
[
chainId
].
hasOwnProperty
(
address
)
&&
!
urlAdded
!
urlAdded
const
zeroBalance
=
JSBI
.
equal
(
JSBI
.
BigInt
(
0
),
balance
.
raw
)
return
(
return
(
<
MenuItem
key
=
{
address
}
onClick
=
{()
=>
_onTokenSelect
(
address
)}
>
<
MenuItem
key
=
{
address
}
onClick
=
{()
=>
(
zeroBalance
?
_onTokenSelect
(
address
,
true
)
:
_onTokenSelect
(
address
)
)}
>
<
RowFixed
>
<
RowFixed
>
<
TokenLogo
address
=
{
address
}
size
=
{
'
24px
'
}
style
=
{{
marginRight
:
'
14px
'
}}
/
>
<
TokenLogo
address
=
{
address
}
size
=
{
'
24px
'
}
style
=
{{
marginRight
:
'
14px
'
}}
/
>
<
Column
>
<
Column
>
...
@@ -353,7 +377,22 @@ function SearchModal({ history, isOpen, onDismiss, onTokenSelect, urlAddedTokens
...
@@ -353,7 +377,22 @@ function SearchModal({ history, isOpen, onDismiss, onTokenSelect, urlAddedTokens
<
/RowFixed
>
<
/RowFixed
>
<
AutoColumn
gap
=
"
4px
"
justify
=
"
end
"
>
<
AutoColumn
gap
=
"
4px
"
justify
=
"
end
"
>
{
balance
?
(
{
balance
?
(
<
Text
>
{
balance
?
balance
.
toSignificant
(
6
)
:
'
-
'
}
<
/Text
>
<
Text
>
{
zeroBalance
&&
showSendWithSwap
?
(
<
ColumnCenter
justify
=
"
center
"
style
=
{{
backgroundColor
:
'
#EBF4FF
'
,
padding
:
'
8px
'
,
borderRadius
:
'
12px
'
}}
>
<
Text
textAlign
=
"
center
"
fontWeight
=
{
500
}
color
=
"
#2172E5
"
>
Send
With
Swap
<
/Text
>
<
/ColumnCenter
>
)
:
balance
?
(
balance
.
toSignificant
(
6
)
)
:
(
'
-
'
)}
<
/Text
>
)
:
account
?
(
)
:
account
?
(
<
SpinnerWrapper
src
=
{
Circle
}
alt
=
"
loader
"
/>
<
SpinnerWrapper
src
=
{
Circle
}
alt
=
"
loader
"
/>
)
:
(
)
:
(
...
...
src/components/Slider/index.js
View file @
655b7956
...
@@ -71,7 +71,7 @@ export default function InputSlider({ value, onChange }) {
...
@@ -71,7 +71,7 @@ export default function InputSlider({ value, onChange }) {
value
=
{
typeof
value
===
'
number
'
?
value
:
0
}
value
=
{
typeof
value
===
'
number
'
?
value
:
0
}
onChange
=
{
onChange
}
onChange
=
{
onChange
}
aria
-
labelledby
=
"
input-slider
"
aria
-
labelledby
=
"
input-slider
"
marks
=
{
marks
}
//
marks={marks}
/>
/>
)
)
}
}
src/components/TokenLogo/index.js
View file @
655b7956
...
@@ -52,7 +52,7 @@ export default function TokenLogo({ address, size = '24px', ...rest }) {
...
@@ -52,7 +52,7 @@ export default function TokenLogo({ address, size = '24px', ...rest }) {
if
(
address
===
'
ETH
'
)
{
if
(
address
===
'
ETH
'
)
{
return
<
StyledEthereumLogo
size
=
{
size
}
{...
rest
}
/
>
return
<
StyledEthereumLogo
size
=
{
size
}
{...
rest
}
/
>
}
else
if
(
!
error
&&
!
BAD_IMAGES
[
address
])
{
}
else
if
(
!
error
&&
!
BAD_IMAGES
[
address
])
{
path
=
TOKEN_ICON_API
(
address
.
toLowerCase
())
path
=
TOKEN_ICON_API
(
address
?
.
toLowerCase
())
}
else
{
}
else
{
return
(
return
(
<
Emoji
{...
rest
}
size
=
{
size
}
>
<
Emoji
{...
rest
}
size
=
{
size
}
>
...
...
src/pages/Send/index.js
deleted
100644 → 0
View file @
4e2c5c1e
import
React
from
'
react
'
import
ExchangePage
from
'
../../components/ExchangePage
'
export
default
function
Send
({
initialCurrency
,
params
})
{
return
<
ExchangePage
initialCurrency
=
{
initialCurrency
}
params
=
{
params
}
sending
=
{
true
}
/
>
}
src/pages/Send/index.tsx
0 → 100644
View file @
655b7956
import
React
,
{
useState
,
useEffect
}
from
'
react
'
import
styled
from
'
styled-components
'
import
{
darken
}
from
'
polished
'
import
{
TokenAmount
,
JSBI
}
from
'
@uniswap/sdk
'
import
QR
from
'
../../assets/svg/QR.svg
'
import
TokenLogo
from
'
../../components/TokenLogo
'
import
SearchModal
from
'
../../components/SearchModal
'
import
ExchangePage
from
'
../../components/ExchangePage
'
import
NumericalInput
from
'
../../components/NumericalInput
'
import
ConfirmationModal
from
'
../../components/ConfirmationModal
'
import
{
Text
}
from
'
rebass
'
import
{
TYPE
}
from
'
../../theme
'
import
{
LightCard
}
from
'
../../components/Card
'
import
{
ArrowDown
}
from
'
react-feather
'
import
{
AutoColumn
}
from
'
../../components/Column
'
import
{
ButtonPrimary
}
from
'
../../components/Button
'
import
{
ReactComponent
as
DropDown
}
from
'
../../assets/images/dropdown.svg
'
import
{
useToken
}
from
'
../../contexts/Tokens
'
import
{
RowBetween
}
from
'
../../components/Row
'
import
{
useENSName
}
from
'
../../hooks
'
import
{
useWeb3React
}
from
'
@web3-react/core
'
import
{
useAddressBalance
}
from
'
../../contexts/Balances
'
import
{
parseUnits
}
from
'
@ethersproject/units
'
import
{
isAddress
}
from
'
../../utils
'
const
CurrencySelect
=
styled
.
button
`
display: flex;
align-items: center;
justify-content: space-between;
font-size: 20px;
width:
${({
selected
})
=>
(
selected
?
'
128px
'
:
'
180px
'
)}
padding: 8px 12px;
background-color:
${({
selected
,
theme
})
=>
(
selected
?
theme
.
buttonBackgroundPlain
:
theme
.
royalBlue
)}
;
color:
${({
selected
,
theme
})
=>
(
selected
?
theme
.
textColor
:
theme
.
white
)}
;
border: 1px solid
${({
selected
,
theme
})
=>
(
selected
?
theme
.
outlineGrey
:
theme
.
royalBlue
)}
;
border-radius: 8px;
outline: none;
cursor: pointer;
user-select: none;
:hover {
border: 1px solid
${({
selected
,
theme
})
=>
(
selected
?
darken
(
0.1
,
theme
.
outlineGrey
)
:
darken
(
0.1
,
theme
.
royalBlue
))}
;
}
:focus {
border: 1px solid
${({
selected
,
theme
})
=>
selected
?
darken
(
0.1
,
theme
.
outlineGrey
)
:
darken
(
0.1
,
theme
.
royalBlue
)}
;
}
:active {
background-color:
${({
selected
,
theme
})
=>
(
selected
?
theme
.
buttonBackgroundPlain
:
theme
.
royalBlue
)}
;
}
`
const
StyledDropDown
=
styled
(
DropDown
)
`
height: 35%;
path {
stroke:
${({
selected
,
theme
})
=>
(
selected
?
theme
.
textColor
:
theme
.
white
)}
;
}
`
const
InputGroup
=
styled
(
AutoColumn
)
`
position: relative;
padding: 40px 0;
`
const
QRWrapper
=
styled
.
div
`
display: flex;
align-items: center;
justify-content: center;
border: 1px solid
${({
theme
})
=>
theme
.
outlineGrey
}
;
background: #fbfbfb;
padding: 4px;
border-radius: 8px;
`
const
StyledInput
=
styled
.
input
`
width:
${({
width
})
=>
width
}
;
border: none;
outline: none;
font-size: 20px;
::placeholder {
color: #edeef2;
}
`
const
StyledNumerical
=
styled
(
NumericalInput
)
`
text-align: center;
font-size: 48px;
font-weight: 500px;
width: 100%;
::placeholder {
color: #edeef2;
}
`
const
MaxButton
=
styled
.
button
`
position: absolute;
right: 70px;
padding: 0.5rem 1rem;
background-color:
${({
theme
})
=>
theme
.
zumthorBlue
}
;
border: 1px solid
${({
theme
})
=>
theme
.
zumthorBlue
}
;
border-radius: 0.5rem;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
margin-right: 0.5rem;
color:
${({
theme
})
=>
theme
.
royalBlue
}
;
:hover {
border: 1px solid
${({
theme
})
=>
theme
.
royalBlue
}
;
}
:focus {
border: 1px solid
${({
theme
})
=>
theme
.
royalBlue
}
;
outline: none;
}
`
export
default
function
Send
()
{
const
{
account
}
=
useWeb3React
()
// setting for send with swap or regular swap
const
[
withSwap
,
setWithSwap
]
=
useState
(
true
)
// modals
const
[
modalOpen
,
setModalOpen
]
=
useState
(
false
)
const
[
showConfirm
,
setShowConfirm
]
=
useState
(
false
)
// token selected
const
[
activeTokenAddress
,
setActiveTokenAddress
]
=
useState
()
const
token
=
useToken
(
activeTokenAddress
)
// user inputs
const
[
typedValue
,
setTypedValue
]
=
useState
(
''
)
const
[
amount
,
setAmount
]
=
useState
(
null
)
const
[
recipient
,
setRecipient
]
=
useState
(
'
0x74Aa01d162E6dC6A657caC857418C403D48E2D77
'
)
//ENS
const
recipientENS
=
useENSName
(
recipient
)
// balances
const
userBalance
=
useAddressBalance
(
account
,
token
)
//errors
const
[
generalError
,
setGeneralError
]
=
useState
(
''
)
const
[
amountError
,
setAmountError
]
=
useState
(
''
)
const
[
recipientError
,
setRecipientError
]
=
useState
(
''
)
function
parseInputAmount
(
newtypedValue
)
{
setTypedValue
(
newtypedValue
)
if
(
!!
token
&&
newtypedValue
!==
''
&&
newtypedValue
!==
'
.
'
)
{
const
typedValueParsed
=
parseUnits
(
newtypedValue
,
token
.
decimals
).
toString
()
setAmount
(
new
TokenAmount
(
token
,
typedValueParsed
))
}
}
function
onMax
()
{
if
(
userBalance
)
{
setTypedValue
(
userBalance
.
toExact
())
setAmount
(
userBalance
)
}
}
const
atMax
=
amount
&&
userBalance
&&
JSBI
.
equal
(
amount
.
raw
,
userBalance
.
raw
)
?
true
:
false
//error detection
useEffect
(()
=>
{
setGeneralError
(
''
)
setRecipientError
(
''
)
setAmountError
(
''
)
if
(
!
amount
)
{
setGeneralError
(
'
Enter an amount
'
)
}
if
(
!
isAddress
(
recipient
))
{
setRecipientError
(
'
Enter a valid address
'
)
}
if
(
!!!
token
)
{
setGeneralError
(
'
Select a token
'
)
}
if
(
amount
&&
userBalance
&&
JSBI
.
greaterThan
(
amount
.
raw
,
userBalance
.
raw
))
{
setAmountError
(
'
Insufficient Balance
'
)
}
},
[
recipient
,
token
,
amount
,
userBalance
])
const
TopContent
=
()
=>
{
return
(
<
AutoColumn
gap=
"30px"
style=
{
{
marginTop
:
'
40px
'
}
}
>
<
RowBetween
>
<
Text
fontSize=
{
36
}
fontWeight=
{
500
}
>
{
amount
?.
toFixed
(
8
)
}
</
Text
>
<
TokenLogo
address=
{
activeTokenAddress
}
size=
{
'
30px
'
}
/>
</
RowBetween
>
<
ArrowDown
size=
{
24
}
color=
"#888D9B"
/>
<
TYPE
.
blue
fontSize=
{
36
}
>
{
recipient
?.
slice
(
0
,
6
)
}
...
{
recipient
?.
slice
(
36
,
42
)
}
</
TYPE
.
blue
>
</
AutoColumn
>
)
}
const
BottomContent
=
()
=>
{
return
(
<
AutoColumn
>
<
ButtonPrimary
>
<
Text
color=
"white"
fontSize=
{
20
}
>
Confirm send
</
Text
>
</
ButtonPrimary
>
</
AutoColumn
>
)
}
const
[
attemptedSend
,
setAttemptedSend
]
=
useState
(
false
)
// clicke confirm
const
[
pendingConfirmation
,
setPendingConfirmation
]
=
useState
(
true
)
// waiting for
return
withSwap
?
(
<
ExchangePage
sendingInput=
{
true
}
/>
)
:
(
<>
<
SearchModal
isOpen=
{
modalOpen
}
onDismiss=
{
()
=>
{
setModalOpen
(
false
)
}
}
filterType=
"tokens"
onTokenSelect=
{
tokenAddress
=>
setActiveTokenAddress
(
tokenAddress
)
}
/>
<
ConfirmationModal
isOpen=
{
showConfirm
}
onDismiss=
{
()
=>
setShowConfirm
(
false
)
}
hash=
""
title=
"Confirm Send"
topContent=
{
TopContent
}
bottomContent=
{
BottomContent
}
attemptingTxn=
{
attemptedSend
}
pendingConfirmation=
{
pendingConfirmation
}
pendingText=
""
/>
</>
)
}
src/pages/Supply/RemoveLiquidity.tsx
View file @
655b7956
...
@@ -41,7 +41,7 @@ const Wrapper = styled.div`
...
@@ -41,7 +41,7 @@ const Wrapper = styled.div`
const
FixedBottom
=
styled
.
div
`
const
FixedBottom
=
styled
.
div
`
position: absolute;
position: absolute;
bottom: -2
4
0px;
bottom: -2
0
0px;
width: 100%;
width: 100%;
`
`
...
...
src/pages/Swap/index.js
View file @
655b7956
...
@@ -2,5 +2,5 @@ import React from 'react'
...
@@ -2,5 +2,5 @@ import React from 'react'
import
ExchangePage
from
'
../../components/ExchangePage
'
import
ExchangePage
from
'
../../components/ExchangePage
'
export
default
function
Swap
({
initialCurrency
,
params
})
{
export
default
function
Swap
({
initialCurrency
,
params
})
{
return
<
ExchangePage
initialCurrency
=
{
initialCurrency
}
params
=
{
params
}
/
>
return
<
ExchangePage
sendingInput
=
{
false
}
/
>
}
}
src/theme/index.js
View file @
655b7956
...
@@ -128,6 +128,21 @@ export const TYPE = {
...
@@ -128,6 +128,21 @@ export const TYPE = {
<
Text
fontWeight
=
{
500
}
color
=
{
theme
().
royalBlue
}
{...
rest
}
>
<
Text
fontWeight
=
{
500
}
color
=
{
theme
().
royalBlue
}
{...
rest
}
>
{
children
}
{
children
}
<
/Text
>
<
/Text
>
),
gray
:
({
children
,
...
rest
})
=>
(
<
Text
fontWeight
=
{
500
}
color
=
{
theme
().
outlineGrey
}
{...
rest
}
>
{
children
}
<
/Text
>
),
italic
:
({
children
,
...
rest
})
=>
(
<
Text
fontWeight
=
{
500
}
fontSize
=
{
12
}
fontStyle
=
{
'
italic
'
}
color
=
{
theme
().
mineshaftGray
}
{...
rest
}
>
{
children
}
<
/Text
>
),
error
:
({
children
,
error
,
...
rest
})
=>
(
<
Text
fontWeight
=
{
500
}
color
=
{
error
?
theme
().
salmonRed
:
theme
().
mineshaftGray
}
{...
rest
}
>
{
children
}
<
/Text
>
)
)
}
}
...
...
src/utils/price.js
deleted
100644 → 0
View file @
4e2c5c1e
import
{
getMarketDetails
}
from
'
@uniswap/sdk
'
import
{
getMedian
,
getMean
}
from
'
./math
'
const
DAI
=
'
DAI
'
const
USDC
=
'
USDC
'
const
TUSD
=
'
TUSD
'
const
USD_STABLECOINS
=
[
DAI
,
USDC
,
TUSD
]
function
forEachStablecoin
(
runner
)
{
return
USD_STABLECOINS
.
map
((
stablecoin
,
index
)
=>
runner
(
index
,
stablecoin
))
}
export
function
getUSDPrice
(
reserves
)
{
const
marketDetails
=
forEachStablecoin
(
i
=>
getMarketDetails
(
reserves
[
i
],
undefined
))
const
ethPrices
=
forEachStablecoin
(
i
=>
marketDetails
[
i
].
marketRate
.
rateInverted
)
const
[
median
]
=
getMedian
(
ethPrices
)
const
[
mean
]
=
getMean
(
ethPrices
)
const
[
weightedMean
]
=
getMean
(
ethPrices
,
forEachStablecoin
(
i
=>
reserves
[
i
].
ethReserve
.
amount
)
)
return
getMean
([
median
,
mean
,
weightedMean
])[
0
]
}
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