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
Show 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)`
}
`
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
)
`
background-color:
${({
theme
})
=>
lighten
(
0.5
,
theme
.
connectedGreen
)}
;
border: 1px solid
${({
theme
})
=>
theme
.
connectedGreen
}
;
...
...
@@ -160,7 +181,7 @@ export function ButtonDropwdownLight({ disabled, children, ...rest }) {
export
function
ButtonRadio
({
active
,
children
,
...
rest
})
{
if
(
!
active
)
{
return
<
Button
Empty
{...
rest
}
>
{
children
}
<
/ButtonEmpty
>
return
<
Button
White
{...
rest
}
>
{
children
}
<
/ButtonWhite
>
}
else
{
return
<
ButtonPrimary
{...
rest
}
>
{
children
}
<
/ButtonPrimary
>
}
...
...
src/components/CurrencyInputPanel/index.js
View file @
655b7956
...
...
@@ -48,10 +48,9 @@ const InputRow = styled.div`
const
CurrencySelect
=
styled
.
button
`
align-items: center;
height: 2.2rem;
font-size: 20px;
background-color:
${({
selected
,
theme
})
=>
(
selected
?
theme
.
buttonBackgroundPlain
:
theme
.
zumthor
Blue
)}
;
color:
${({
selected
,
theme
})
=>
(
selected
?
theme
.
textColor
:
theme
.
royalBlu
e
)}
;
background-color:
${({
selected
,
theme
})
=>
(
selected
?
theme
.
buttonBackgroundPlain
:
theme
.
royal
Blue
)}
;
color:
${({
selected
,
theme
})
=>
(
selected
?
theme
.
textColor
:
theme
.
whit
e
)}
;
border: 1px solid
${({
selected
,
theme
,
disableTokenSelect
})
=>
disableTokenSelect
?
theme
.
buttonBackgroundPlain
:
selected
?
theme
.
buttonOutlinePlain
:
theme
.
royalBlue
}
;
...
...
@@ -70,7 +69,8 @@ const CurrencySelect = styled.button`
}
: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)`
height: 35%;
path {
stroke:
${({
selected
,
theme
})
=>
(
selected
?
theme
.
textColor
:
theme
.
royalBlu
e
)}
;
stroke:
${({
selected
,
theme
})
=>
(
selected
?
theme
.
textColor
:
theme
.
whit
e
)}
;
}
`
const
InputPanel
=
styled
.
div
`
${({
theme
})
=>
theme
.
flexColumnNoWrap
}
position: relative;
border-radius:
1.25rem
;
border-radius:
${({
hideInput
})
=>
(
hideInput
?
'
8px
'
:
'
20px
'
)}
;
background-color:
${({
theme
})
=>
theme
.
inputBackground
}
;
z-index: 1;
`
const
Container
=
styled
.
div
`
border-radius:
1.25rem
;
border-radius:
${({
hideInput
})
=>
(
hideInput
?
'
8px
'
:
'
20px
'
)}
;
border: 1px solid
${({
error
,
theme
})
=>
(
error
?
theme
.
salmonRed
:
theme
.
mercuryGray
)}
;
background-color:
${({
theme
})
=>
theme
.
inputBackground
}
;
...
...
@@ -174,7 +174,10 @@ export default function CurrencyInputPanel({
hideBalance
=
false
,
isExchange
=
false
,
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
{
t
}
=
useTranslation
()
...
...
@@ -236,7 +239,7 @@ export default function CurrencyInputPanel({
return
(
<
InputPanel
>
<
Container
error
=
{
!!
error
}
>
<
Container
error
=
{
!!
error
}
hideInput
=
{
hideInput
}
>
{
!
hideBalance
&&
(
<
LabelRow
>
<
RowBetween
>
...
...
@@ -250,7 +253,9 @@ export default function CurrencyInputPanel({
<
/RowBetween
>
<
/LabelRow
>
)}
<
InputRow
>
<
InputRow
style
=
{
hideInput
?
{
padding
:
'
0
'
,
borderRadius
:
'
8px
'
}
:
{}}
hideInput
=
{
hideInput
}
>
{
!
hideInput
&&
(
<>
<
NumericalInput
value
=
{
value
}
onUserInput
=
{
val
=>
{
...
...
@@ -259,6 +264,8 @@ export default function CurrencyInputPanel({
/
>
{
!!
token
?.
address
&&
!
atMax
&&
<
StyledBalanceMax
onClick
=
{
onMax
}
>
MAX
<
/StyledBalanceMax>
}
{
renderUnlockButton
()}
<
/
>
)}
<
CurrencySelect
selected
=
{
!!
token
?.
address
}
onClick
=
{()
=>
{
...
...
@@ -296,6 +303,8 @@ export default function CurrencyInputPanel({
urlAddedTokens
=
{
urlAddedTokens
}
field
=
{
field
}
onTokenSelect
=
{
onTokenSelection
}
showSendWithSwap
=
{
showSendWithSwap
}
onTokenSelectSendWithSwap
=
{
onTokenSelectSendWithSwap
}
/
>
)}
<
/InputPanel
>
...
...
src/components/ExchangePage/index.tsx
View file @
655b7956
...
...
@@ -4,6 +4,7 @@ import { ethers } from 'ethers'
import
{
parseUnits
,
parseEther
}
from
'
@ethersproject/units
'
import
{
WETH
,
TradeType
,
Route
,
Trade
,
TokenAmount
,
JSBI
}
from
'
@uniswap/sdk
'
import
QR
from
'
../../assets/svg/QR.svg
'
import
TokenLogo
from
'
../TokenLogo
'
import
QuestionHelper
from
'
../Question
'
import
NumericalInput
from
'
../NumericalInput
'
...
...
@@ -11,23 +12,23 @@ 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
{
TYPE
}
from
'
../../theme
'
import
{
GreyCard
,
LightCard
}
from
'
../../components/Card
'
import
{
ArrowDown
,
ArrowUp
}
from
'
react-feather
'
import
{
ButtonPrimary
,
ButtonError
,
ButtonRadio
}
from
'
../Button
'
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
'
import
{
useWeb3React
,
useTokenContract
}
from
'
../../hooks
'
import
{
useAddressBalance
}
from
'
../../contexts/Balances
'
import
{
useTransactionAdder
}
from
'
../../contexts/Transactions
'
import
{
useAddressAllowance
}
from
'
../../contexts/Allowances
'
import
{
ROUTER_ADDRESSES
}
from
'
../../constants
'
import
{
getRouterContract
,
calculateGasMargin
}
from
'
../../utils
'
import
{
getRouterContract
,
calculateGasMargin
,
isAddress
,
getProviderOrSigner
}
from
'
../../utils
'
const
Wrapper
=
styled
.
div
`
position: relative;
...
...
@@ -64,7 +65,66 @@ const InputWrapper = styled(RowBetween)`
border-radius: 8px;
padding: 4px 8px;
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
{
...
...
@@ -91,7 +151,7 @@ function initializeSwapState(inputAddress?: string, outputAddress?: string): Swa
address
:
inputAddress
},
[
Field
.
OUTPUT
]:
{
address
:
'
0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735
'
address
:
outputAddress
}
}
}
...
...
@@ -192,22 +252,28 @@ const INITIAL_ALLOWED_SLIPPAGE = 200
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
const
ALLOWED_
SLIPPAGE
_MEDIUM
=
100
const
ALLOWED_
SLIPPAGE
_HIGH
=
500
export
default
function
ExchangePage
()
{
export
default
function
ExchangePage
(
{
sendingInput
=
false
}
)
{
const
{
chainId
,
account
,
library
}
=
useWeb3React
()
const
routerAddress
=
ROUTER_ADDRESSES
[
chainId
]
// adding notifications on txns
const
[,
addPopup
]
=
usePopups
()
const
addTransaction
=
useTransactionAdder
()
// sending state
const
[
sending
,
setSending
]
=
useState
(
sendingInput
)
const
[
sendingWithSwap
,
setSendingWithSwap
]
=
useState
(
false
)
const
[
recipient
,
setRecipient
]
=
useState
(
''
)
// input details
const
[
state
,
dispatch
]
=
useReducer
(
reducer
,
WETH
[
chainId
].
address
,
initializeSwapState
)
const
{
independentField
,
typedValue
,
...
fieldData
}
=
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 thin
sg liek reserve sizes
const
[
tradeError
,
setTradeError
]
=
useState
(
''
)
// error for thin
gs like reserve size or route
const
tokens
=
{
[
Field
.
INPUT
]:
useToken
(
fieldData
[
Field
.
INPUT
].
address
),
...
...
@@ -217,12 +283,18 @@ export default function ExchangePage() {
const
exchange
=
useExchange
(
tokens
[
Field
.
INPUT
],
tokens
[
Field
.
OUTPUT
])
const
route
=
!!
exchange
?
new
Route
([
exchange
],
tokens
[
Field
.
INPUT
])
:
undefined
// modal state
const
addTransaction
=
useTransactionAdder
()
const
[
showConfirm
,
setShowConfirm
]
=
useState
(
true
)
// modal and loading
const
[
showConfirm
,
setShowConfirm
]
=
useState
(
false
)
const
[
pendingConfirmation
,
setPendingConfirmation
]
=
useState
(
true
)
// waiting for user confirmation
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
const
[
txHash
,
setTxHash
]
=
useState
()
const
[
deadline
,
setDeadline
]
=
useState
(
DEFAULT_DEADLINE_FROM_NOW
)
...
...
@@ -240,10 +312,9 @@ export default function ExchangePage() {
const
parsedAmounts
:
{
[
field
:
number
]:
TokenAmount
}
=
{}
// try to parse typed value
// if (typedValue !== '' && typedValue !== '.' && tokens[independentField]) {
if
(
tokens
[
independentField
])
{
if
(
typedValue
!==
''
&&
typedValue
!==
'
.
'
&&
tokens
[
independentField
])
{
try
{
const
typedValueParsed
=
parseUnits
(
'
0.0001
'
,
tokens
[
independentField
].
decimals
).
toString
()
const
typedValueParsed
=
parseUnits
(
typedValue
,
tokens
[
independentField
].
decimals
).
toString
()
if
(
typedValueParsed
!==
'
0
'
)
parsedAmounts
[
independentField
]
=
new
TokenAmount
(
tokens
[
independentField
],
typedValueParsed
)
}
catch
(
error
)
{
...
...
@@ -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
=
!!
userBalances
[
Field
.
INPUT
]
&&
JSBI
.
greaterThan
(
...
...
@@ -388,16 +459,14 @@ export default function ExchangePage() {
outputApproval
&&
JSBI
.
greaterThan
(
parsedAmounts
[
Field
.
OUTPUT
].
raw
,
outputApproval
.
raw
)
// 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
)
// parse the input for custom slippage
function
parseCustomInput
(
val
)
{
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
)))
{
setCustomSlippage
(
val
)
setAllowedSlippage
(
val
*
100
)
...
...
@@ -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
()
{
const
routerContract
=
getRouterContract
(
chainId
,
library
,
account
)
setAttemptingTxn
(
true
)
...
...
@@ -497,7 +622,7 @@ export default function ExchangePage() {
gasLimit
:
calculateGasMargin
(
estimatedGasLimit
,
GAS_MARGIN
)
})
.
then
(
response
=>
{
setTxHash
(
response
)
setTxHash
(
response
.
hash
)
addTransaction
(
response
)
setPendingConfirmation
(
false
)
})
...
...
@@ -513,17 +638,38 @@ export default function ExchangePage() {
}
// errors
const
[
generalError
,
setGeneralError
]
=
useState
(
''
)
const
[
inputError
,
setInputError
]
=
useState
(
''
)
const
[
outputError
,
setOutputError
]
=
useState
(
''
)
const
[
recipientError
,
setRecipientError
]
=
useState
(
''
)
const
[
isValid
,
setIsValid
]
=
useState
(
false
)
const
ignoreOutput
=
sending
?
!
sendingWithSwap
:
false
useEffect
(()
=>
{
// reset errors
setGeneralError
(
null
)
setInputError
(
null
)
setOutputError
(
null
)
setTradeError
(
null
)
setRecipientError
(
null
)
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
(
parsedAmounts
[
Field
.
INPUT
]
&&
exchange
&&
...
...
@@ -534,6 +680,7 @@ export default function ExchangePage() {
}
if
(
!
ignoreOutput
&&
parsedAmounts
[
Field
.
OUTPUT
]
&&
exchange
&&
JSBI
.
greaterThan
(
parsedAmounts
[
Field
.
OUTPUT
].
raw
,
exchange
.
reserveOf
(
tokens
[
Field
.
OUTPUT
]).
raw
)
...
...
@@ -542,12 +689,12 @@ export default function ExchangePage() {
setIsValid
(
false
)
}
if
(
showInputUnlock
)
{
if
(
showInputUnlock
&&
!
(
sending
&&
!
sendingWithSwap
)
)
{
setInputError
(
'
Approval Needed
'
)
setIsValid
(
false
)
}
if
(
showOutputUnlock
)
{
if
(
showOutputUnlock
&&
!
ignoreOutput
)
{
setOutputError
(
'
Approval Needed
'
)
setIsValid
(
false
)
}
...
...
@@ -560,19 +707,22 @@ export default function ExchangePage() {
setInputError
(
'
Insufficient balance.
'
)
setIsValid
(
false
)
}
if
(
userBalances
[
Field
.
OUTPUT
]
&&
parsedAmounts
[
Field
.
OUTPUT
]
&&
JSBI
.
lessThan
(
userBalances
[
Field
.
OUTPUT
].
raw
,
parsedAmounts
[
Field
.
OUTPUT
]?.
raw
)
)
{
setOutputError
(
'
Insufficient balance.
'
)
setIsValid
(
false
)
}
},
[
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
},
[
exchange
,
ignoreOutput
,
parsedAmounts
,
recipient
,
sending
,
sendingWithSwap
,
showInputUnlock
,
showOutputUnlock
,
tokens
,
userBalances
])
// 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
()
{
setPendingConfirmation
(
true
)
...
...
@@ -581,6 +731,27 @@ export default function ExchangePage() {
}
function
modalHeader
()
{
if
(
sending
&&
!
sendingWithSwap
)
{
return
(
<
AutoColumn
gap=
"30px"
style=
{
{
marginTop
:
'
40px
'
}
}
>
<
RowBetween
>
<
Text
fontSize=
{
36
}
fontWeight=
{
500
}
>
{
parsedAmounts
[
Field
.
INPUT
]?.
toFixed
(
8
)
}
</
Text
>
<
TokenLogo
address=
{
tokens
[
Field
.
INPUT
]?.
address
}
size=
{
'
30px
'
}
/>
</
RowBetween
>
<
ArrowDown
size=
{
24
}
color=
"#888D9B"
/>
<
TYPE
.
blue
fontSize=
{
36
}
>
{
recipient
?.
slice
(
0
,
6
)
}
...
{
recipient
?.
slice
(
36
,
42
)
}
</
TYPE
.
blue
>
</
AutoColumn
>
)
}
if
(
sending
&&
sendingWithSwap
)
{
}
if
(
!
sending
)
{
return
(
<
AutoColumn
gap=
{
'
20px
'
}
style=
{
{
marginTop
:
'
40px
'
}
}
>
<
RowBetween
align=
"flex-end"
>
...
...
@@ -611,9 +782,26 @@ export default function ExchangePage() {
</
AutoColumn
>
)
}
}
function
modalBottom
()
{
return
showAdvanced
?
(
if
(
sending
&&
!
sendingWithSwap
)
{
return
(
<
AutoColumn
>
<
ButtonPrimary
onClick=
{
onSend
}
>
<
Text
color=
"white"
fontSize=
{
20
}
>
Confirm send
</
Text
>
</
ButtonPrimary
>
</
AutoColumn
>
)
}
if
(
sending
&&
sendingWithSwap
)
{
}
if
(
showAdvanced
)
{
return
(
<
AutoColumn
gap=
"20px"
>
<
Link
onClick=
{
()
=>
{
...
...
@@ -623,7 +811,7 @@ export default function ExchangePage() {
back
</
Link
>
<
RowBetween
>
<
TYPE
.
main
>
Limit additional price slippage
</
TYPE
.
main
>
<
TYPE
.
main
>
Limit additional price impact
</
TYPE
.
main
>
<
QuestionHelper
text=
""
/>
</
RowBetween
>
<
Row
>
...
...
@@ -667,7 +855,7 @@ export default function ExchangePage() {
</
ButtonRadio
>
</
Row
>
<
RowFixed
>
<
InputWrapper
active=
{
SLIPPAGE_INDEX
[
4
]
===
activeIndex
}
>
<
InputWrapper
active=
{
SLIPPAGE_INDEX
[
4
]
===
activeIndex
}
error=
{
slippageInputError
}
>
<
NumericalInput
align=
{
customSlippage
?
'
right
'
:
'
left
'
}
value=
{
customSlippage
||
''
}
...
...
@@ -685,20 +873,31 @@ export default function ExchangePage() {
/>
%
</
InputWrapper
>
{
slippageInputError
&&
(
<
TYPE
.
error
error=
{
true
}
fontSize=
{
12
}
style=
{
{
marginLeft
:
'
10px
'
}
}
>
Your transaction may be front-run
</
TYPE
.
error
>
)
}
</
RowFixed
>
<
RowBetween
>
<
TYPE
.
main
>
Adjust deadline (minutes from now)
</
TYPE
.
main
>
</
RowBetween
>
<
RowFixed
>
<
InputWrapper
>
<
NumericalInput
value=
{
customDeadline
}
onUserInput=
{
val
=>
{
parseCustomDeadline
(
val
)
}
}
/>
</
InputWrapper
>
</
RowFixed
>
</
AutoColumn
>
)
:
(
)
}
if
(
!
sending
)
{
return
(
<>
<
RowBetween
>
<
Text
color=
"#565A69"
fontWeight=
{
500
}
fontSize=
{
16
}
>
...
...
@@ -723,12 +922,12 @@ export default function ExchangePage() {
{
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"
>
<
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
)
...
...
@@ -740,10 +939,13 @@ export default function ExchangePage() {
</>
)
}
}
const
pendingText
=
` Swapped
${
parsedAmounts
[
Field
.
INPUT
]?.
toSignificant
(
6
)}
$
{
tokens
[
Field
.
INPUT
]?.
symbol
}
for
$
{
parsedAmounts
[
Field
.
OUTPUT
]?.
toSignificant
(
6
)}
$
{
tokens
[
Field
.
OUTPUT
]?.
symbol
}
`
const
pendingText
=
sending
?
`Sending
${
parsedAmounts
[
Field
.
INPUT
]?.
toSignificant
(
6
)}
$
{
tokens
[
Field
.
INPUT
]?.
symbol
}
to
$
{
recipient
}
`
: `
Swapped
$
{
parsedAmounts
[
Field
.
INPUT
]?.
toSignificant
(
6
)}
$
{
tokens
[
Field
.
INPUT
]?.
symbol
}
for
$
{
parsedAmounts
[
Field
.
OUTPUT
]?.
toSignificant
(
6
)}
$
{
tokens
[
Field
.
OUTPUT
]?.
symbol
}
`
return (
<Wrapper>
...
...
@@ -756,12 +958,55 @@ export default function ExchangePage() {
attemptingTxn={attemptingTxn}
pendingConfirmation={pendingConfirmation}
hash={txHash ? txHash : ''}
topContent={
() => modalHeader()
}
topContent={
modalHeader
}
bottomContent={modalBottom}
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'}>
{(!sending || sendingWithSwap) && (
<>
<CurrencyInputPanel
field={Field.INPUT}
value={formattedAmounts[Field.INPUT]}
...
...
@@ -804,7 +1049,9 @@ export default function ExchangePage() {
</Text>
<Text fontWeight={500} color="#565A69">
{exchange
? `
1
$
{
tokens
[
Field
.
INPUT
].
symbol
}
=
$
{
route
?.
midPrice
.
toSignificant
(
6
)}
$
{
tokens
[
Field
.
OUTPUT
].
symbol
}
`
? `
1
$
{
tokens
[
Field
.
INPUT
].
symbol
}
=
$
{
route
?.
midPrice
.
toSignificant
(
6
)}
$
{
tokens
[
Field
.
OUTPUT
].
symbol
}
`
: '-'}
</Text>
</RowBetween>
...
...
@@ -818,7 +1065,21 @@ export default function ExchangePage() {
</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
onClick={() => {
setShowConfirm(true)
...
...
@@ -827,10 +1088,14 @@ export default function ExchangePage() {
error={!!warningHigh}
>
<Text fontSize={20} fontWeight={500}>
{inputError
{generalError
? generalError
: inputError
? inputError
: outputError
? outputError
: recipientError
? recipientError
: tradeError
? tradeError
: warningHigh
...
...
@@ -839,6 +1104,7 @@ export default function ExchangePage() {
</Text>
</ButtonError>
</AutoColumn>
{warningHigh && (
<FixedBottom>
<GreyCard>
...
...
src/components/NumericalInput/index.tsx
View file @
655b7956
...
...
@@ -5,11 +5,11 @@ const StyledInput = styled.input`
color:
${({
error
,
theme
})
=>
error
&&
theme
.
salmonRed
}
;
background-color:
${({
theme
})
=>
theme
.
inputBackground
}
;
color:
${({
theme
})
=>
theme
.
textColor
}
;
width: 0;
font-size: 20px;
outline: none;
border: none;
flex: 1 1 auto;
width: 0;
background-color:
${({
theme
})
=>
theme
.
inputBackground
}
;
font-size:
${({
fontSize
})
=>
fontSize
&&
fontSize
}
;
text-align:
${({
align
})
=>
align
&&
align
}
;
...
...
src/components/SearchModal/index.js
View file @
655b7956
import
React
,
{
useState
,
useRef
,
useMemo
,
useEffect
}
from
'
react
'
import
{
withRouter
}
from
'
react-router-dom
'
import
{
Link
}
from
'
react-router-dom
'
import
{
Link
as
StyledLink
}
from
'
../../theme/components
'
import
{
useTranslation
}
from
'
react-i18next
'
import
{
ethers
}
from
'
ethers
'
import
'
@reach/tooltip/styles.css
'
import
styled
from
'
styled-components
'
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
{
withRouter
}
from
'
react-router-dom
'
import
{
JSBI
}
from
'
@uniswap/sdk
'
import
{
Text
}
from
'
rebass
'
import
Column
,
{
AutoColumn
}
from
'
../Column
'
import
{
RowBetween
,
RowFixed
}
from
'
../Row
'
import
{
Link
as
StyledLink
}
from
'
../../theme/components
'
import
Modal
from
'
../Modal
'
import
Circle
from
'
../../assets/images/circle.svg
'
import
TokenLogo
from
'
../TokenLogo
'
import
{
CloseIcon
}
from
'
../../theme/components
'
import
DoubleTokenLogo
from
'
../DoubleLogo
'
import
{
useWeb3React
}
from
'
../../hooks
'
import
{
isAddress
}
from
'
../../utils
'
import
Modal
from
'
../Modal
'
import
{
useToken
,
useAllTokens
,
INITIAL_TOKENS_CONTEXT
}
from
'
../../contexts/Tokens
'
import
Column
,
{
AutoColumn
}
from
'
../Column
'
import
{
Text
}
from
'
rebass
'
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
{
useTranslation
}
from
'
react-i18next
'
import
{
useAllExchanges
}
from
'
../../contexts/Exchanges
'
import
{
useToken
,
useAllTokens
,
INITIAL_TOKENS_CONTEXT
}
from
'
../../contexts/Tokens
'
const
TokenModalInfo
=
styled
.
div
`
${({
theme
})
=>
theme
.
flexRowNoWrap
}
...
...
@@ -108,7 +112,17 @@ const MenuItem = styled(PaddedItem)`
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
{
account
,
chainId
}
=
useWeb3React
()
...
...
@@ -171,6 +185,7 @@ function SearchModal({ history, isOpen, onDismiss, onTokenSelect, urlAddedTokens
let
balance
// only update if we have data
balance
=
allBalances
?.[
account
]?.[
k
]
return
{
name
:
allTokens
[
k
].
name
,
symbol
:
allTokens
[
k
].
symbol
,
...
...
@@ -203,11 +218,17 @@ function SearchModal({ history, isOpen, onDismiss, onTokenSelect, urlAddedTokens
})
},
[
tokenList
,
searchQuery
])
function
_onTokenSelect
(
address
)
{
function
_onTokenSelect
(
address
,
sendWithSwap
=
false
)
{
if
(
sendWithSwap
)
{
setSearchQuery
(
''
)
onTokenSelectSendWithSwap
(
address
)
onDismiss
()
}
else
{
setSearchQuery
(
''
)
onTokenSelect
(
address
)
onDismiss
()
}
}
// manage focus on modal show
const
inputRef
=
useRef
()
...
...
@@ -340,8 +361,11 @@ function SearchModal({ history, isOpen, onDismiss, onTokenSelect, urlAddedTokens
INITIAL_TOKENS_CONTEXT
[
chainId
]
&&
!
INITIAL_TOKENS_CONTEXT
[
chainId
].
hasOwnProperty
(
address
)
&&
!
urlAdded
const
zeroBalance
=
JSBI
.
equal
(
JSBI
.
BigInt
(
0
),
balance
.
raw
)
return
(
<
MenuItem
key
=
{
address
}
onClick
=
{()
=>
_onTokenSelect
(
address
)}
>
<
MenuItem
key
=
{
address
}
onClick
=
{()
=>
(
zeroBalance
?
_onTokenSelect
(
address
,
true
)
:
_onTokenSelect
(
address
)
)}
>
<
RowFixed
>
<
TokenLogo
address
=
{
address
}
size
=
{
'
24px
'
}
style
=
{{
marginRight
:
'
14px
'
}}
/
>
<
Column
>
...
...
@@ -353,7 +377,22 @@ function SearchModal({ history, isOpen, onDismiss, onTokenSelect, urlAddedTokens
<
/RowFixed
>
<
AutoColumn
gap
=
"
4px
"
justify
=
"
end
"
>
{
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
?
(
<
SpinnerWrapper
src
=
{
Circle
}
alt
=
"
loader
"
/>
)
:
(
...
...
src/components/Slider/index.js
View file @
655b7956
...
...
@@ -71,7 +71,7 @@ export default function InputSlider({ value, onChange }) {
value
=
{
typeof
value
===
'
number
'
?
value
:
0
}
onChange
=
{
onChange
}
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 }) {
if
(
address
===
'
ETH
'
)
{
return
<
StyledEthereumLogo
size
=
{
size
}
{...
rest
}
/
>
}
else
if
(
!
error
&&
!
BAD_IMAGES
[
address
])
{
path
=
TOKEN_ICON_API
(
address
.
toLowerCase
())
path
=
TOKEN_ICON_API
(
address
?
.
toLowerCase
())
}
else
{
return
(
<
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`
const
FixedBottom
=
styled
.
div
`
position: absolute;
bottom: -2
4
0px;
bottom: -2
0
0px;
width: 100%;
`
...
...
src/pages/Swap/index.js
View file @
655b7956
...
...
@@ -2,5 +2,5 @@ import React from 'react'
import
ExchangePage
from
'
../../components/ExchangePage
'
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 = {
<
Text
fontWeight
=
{
500
}
color
=
{
theme
().
royalBlue
}
{...
rest
}
>
{
children
}
<
/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