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
8404c607
Unverified
Commit
8404c607
authored
Feb 10, 2022
by
Zach Pomerantz
Committed by
GitHub
Feb 10, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: confirm price impact (#3288)
* refactor: action button naming * feat: high price impact acknowledgement
parent
b4aac94c
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
52 additions
and
47 deletions
+52
-47
ActionButton.tsx
src/lib/components/ActionButton.tsx
+16
-24
ErrorBoundary.tsx
src/lib/components/Error/ErrorBoundary.tsx
+1
-1
ErrorDialog.tsx
src/lib/components/Error/ErrorDialog.tsx
+3
-3
StatusDialog.tsx
src/lib/components/Swap/Status/StatusDialog.tsx
+1
-1
index.tsx
src/lib/components/Swap/Summary/index.tsx
+26
-15
SwapButton.tsx
src/lib/components/Swap/SwapButton.tsx
+3
-3
index.tsx
src/lib/icons/index.tsx
+2
-0
No files found.
src/lib/components/ActionButton.tsx
View file @
8404c607
...
...
@@ -15,7 +15,7 @@ const StyledButton = styled(Button)`
}
`
const
Update
Row
=
styled
(
Row
)
``
const
Action
Row
=
styled
(
Row
)
``
const
grow
=
keyframes
`
from {
...
...
@@ -28,12 +28,12 @@ const grow = keyframes`
}
`
const
update
Css
=
css
`
const
action
Css
=
css
`
border: 1px solid
${({
theme
})
=>
theme
.
outline
}
;
padding: calc(0.25em - 1px);
padding-left: calc(0.75em - 1px);
${
Update
Row
}
{
${
Action
Row
}
{
animation:
${
grow
}
0.25s ease-in;
white-space: nowrap;
}
...
...
@@ -45,45 +45,37 @@ const updateCss = css`
}
`
export
const
Overlay
=
styled
(
Row
)
<
{
update
?:
boolean
}
>
`
export
const
Overlay
=
styled
(
Row
)
<
{
action
?:
boolean
}
>
`
border-radius:
${({
theme
})
=>
theme
.
borderRadius
}
em;
flex-direction: row-reverse;
min-height: 3.5em;
transition: padding 0.25s ease-out;
${({
update
})
=>
update
&&
update
Css
}
${({
action
})
=>
action
&&
action
Css
}
`
export
interface
ActionButtonProps
{
color
?:
Color
disabled
?:
boolean
update
?:
{
message
:
ReactNode
;
action
:
ReactNode
;
icon
?:
Icon
}
action
?:
{
message
:
ReactNode
;
icon
?:
Icon
;
onClick
:
()
=>
void
;
children
:
ReactNode
}
onClick
:
()
=>
void
onUpdate
?:
()
=>
void
children
:
ReactNode
}
export
default
function
ActionButton
({
color
=
'
accent
'
,
disabled
,
update
,
onClick
,
onUpdate
,
children
,
}:
ActionButtonProps
)
{
export
default
function
ActionButton
({
color
=
'
accent
'
,
disabled
,
action
,
onClick
,
children
}:
ActionButtonProps
)
{
const
textColor
=
useMemo
(()
=>
(
color
===
'
accent
'
&&
!
disabled
?
'
onAccent
'
:
'
currentColor
'
),
[
color
,
disabled
])
return
(
<
Overlay
update=
{
Boolean
(
update
)
}
flex
align=
"stretch"
>
<
StyledButton
color=
{
color
}
disabled=
{
disabled
}
onClick=
{
update
?
onUpdate
:
onClick
}
>
<
ThemedText
.
TransitionButton
buttonSize=
{
update
?
'
medium
'
:
'
large
'
}
color=
{
textColor
}
>
{
update
?
update
.
actio
n
:
children
}
<
Overlay
action=
{
Boolean
(
action
)
}
flex
align=
"stretch"
>
<
StyledButton
color=
{
color
}
disabled=
{
disabled
}
onClick=
{
action
?
action
.
onClick
:
onClick
}
>
<
ThemedText
.
TransitionButton
buttonSize=
{
action
?
'
medium
'
:
'
large
'
}
color=
{
textColor
}
>
{
action
?
action
.
childre
n
:
children
}
</
ThemedText
.
TransitionButton
>
</
StyledButton
>
{
update
&&
(
<
Update
Row
gap=
{
0.5
}
>
<
LargeIcon
color=
"currentColor"
icon=
{
update
.
icon
||
AlertTriangle
}
/>
<
ThemedText
.
Subhead2
>
{
update
?.
message
}
</
ThemedText
.
Subhead2
>
</
Update
Row
>
{
action
&&
(
<
Action
Row
gap=
{
0.5
}
>
<
LargeIcon
color=
"currentColor"
icon=
{
action
.
icon
||
AlertTriangle
}
/>
<
ThemedText
.
Subhead2
>
{
action
?.
message
}
</
ThemedText
.
Subhead2
>
</
Action
Row
>
)
}
</
Overlay
>
)
...
...
src/lib/components/Error/ErrorBoundary.tsx
View file @
8404c607
...
...
@@ -36,7 +36,7 @@ export default class ErrorBoundary extends React.Component<ErrorBoundaryProps, E
error=
{
this
.
state
.
error
}
header=
{
<
Trans
>
Something went wrong.
</
Trans
>
}
action=
{
<
Trans
>
Reload the page
</
Trans
>
}
on
Action
=
{
()
=>
window
.
location
.
reload
()
}
on
Click
=
{
()
=>
window
.
location
.
reload
()
}
/>
</
Dialog
>
)
...
...
src/lib/components/Error/ErrorDialog.tsx
View file @
8404c607
...
...
@@ -87,10 +87,10 @@ interface ErrorDialogProps {
header
?:
ReactNode
error
:
Error
action
:
ReactNode
on
Action
:
()
=>
void
on
Click
:
()
=>
void
}
export
default
function
ErrorDialog
({
header
,
error
,
action
,
on
Action
}:
ErrorDialogProps
)
{
export
default
function
ErrorDialog
({
header
,
error
,
action
,
on
Click
}:
ErrorDialogProps
)
{
const
[
open
,
setOpen
]
=
useState
(
false
)
const
[
details
,
setDetails
]
=
useState
<
HTMLDivElement
|
null
>
(
null
)
const
scrollbar
=
useScrollbar
(
details
)
...
...
@@ -123,7 +123,7 @@ export default function ErrorDialog({ header, error, action, onAction }: ErrorDi
</
ThemedText
.
Code
>
</
Column
>
</
ErrorColumn
>
<
ActionButton
onClick=
{
on
Action
}
>
{
action
}
</
ActionButton
>
<
ActionButton
onClick=
{
on
Click
}
>
{
action
}
</
ActionButton
>
</
ExpandoColumn
>
</
Column
>
)
...
...
src/lib/components/Swap/Status/StatusDialog.tsx
View file @
8404c607
...
...
@@ -93,7 +93,7 @@ export default function TransactionStatusDialog({ tx, onClose }: TransactionStat
header=
{
errorMessage
}
error=
{
new
Error
(
'
TODO(zzmp)
'
)
}
action=
{
<
Trans
>
Dismiss
</
Trans
>
}
on
Action
=
{
onClose
}
on
Click
=
{
onClose
}
/>
)
:
(
<
TransactionStatus
tx=
{
tx
}
onClose=
{
onClose
}
/>
...
...
src/lib/components/Swap/Summary/index.tsx
View file @
8404c607
...
...
@@ -6,7 +6,7 @@ import { ALLOWED_PRICE_IMPACT_HIGH, ALLOWED_PRICE_IMPACT_MEDIUM } from 'constant
import
{
useAtomValue
}
from
'
jotai/utils
'
import
{
IconButton
}
from
'
lib/components/Button
'
import
useScrollbar
from
'
lib/hooks/useScrollbar
'
import
{
AlertTriangle
,
Expando
,
Info
}
from
'
lib/icons
'
import
{
AlertTriangle
,
BarChart
,
Expando
,
Info
}
from
'
lib/icons
'
import
{
MIN_HIGH_SLIPPAGE
}
from
'
lib/state/settings
'
import
{
Field
,
independentFieldAtom
}
from
'
lib/state/swap
'
import
styled
,
{
ThemedText
}
from
'
lib/theme
'
...
...
@@ -79,8 +79,6 @@ const Body = styled(Column)<{ open: boolean }>`
}
`
const
priceUpdate
=
{
message
:
<
Trans
>
Price updated
</
Trans
>,
action
:
<
Trans
>
Accept
</
Trans
>
}
interface
SummaryDialogProps
{
trade
:
Trade
<
Currency
,
Currency
,
TradeType
>
allowedSlippage
:
Percent
...
...
@@ -92,8 +90,12 @@ export function SummaryDialog({ trade, allowedSlippage, onConfirm }: SummaryDial
const
inputCurrency
=
inputAmount
.
currency
const
outputCurrency
=
outputAmount
.
currency
const
priceImpact
=
useMemo
(()
=>
computeRealizedPriceImpact
(
trade
),
[
trade
])
const
independentField
=
useAtomValue
(
independentFieldAtom
)
const
{
i18n
}
=
useLingui
()
const
[
open
,
setOpen
]
=
useState
(
false
)
const
[
details
,
setDetails
]
=
useState
<
HTMLDivElement
|
null
>
(
null
)
const
scrollbar
=
useScrollbar
(
details
)
const
warning
=
useMemo
(()
=>
{
if
(
priceImpact
.
greaterThan
(
ALLOWED_PRICE_IMPACT_HIGH
))
return
'
error
'
...
...
@@ -102,18 +104,31 @@ export function SummaryDialog({ trade, allowedSlippage, onConfirm }: SummaryDial
return
},
[
allowedSlippage
,
priceImpact
])
const
[
ackPriceImpact
,
setAckPriceImpact
]
=
useState
(
false
)
const
[
confirmedTrade
,
setConfirmedTrade
]
=
useState
(
trade
)
const
doesTradeDiffer
=
useMemo
(
()
=>
Boolean
(
trade
&&
confirmedTrade
&&
tradeMeaningfullyDiffers
(
trade
,
confirmedTrade
)),
[
confirmedTrade
,
trade
]
)
const
[
open
,
setOpen
]
=
useState
(
false
)
const
[
details
,
setDetails
]
=
useState
<
HTMLDivElement
|
null
>
(
null
)
const
scrollbar
=
useScrollbar
(
details
)
const
{
i18n
}
=
useLingui
()
const
action
=
useMemo
(()
=>
{
if
(
doesTradeDiffer
)
{
return
{
message
:
<
Trans
>
Price updated
</
Trans
>,
icon
:
BarChart
,
onClick
:
()
=>
setConfirmedTrade
(
trade
),
children
:
<
Trans
>
Accept
</
Trans
>,
}
}
else
if
(
priceImpact
.
greaterThan
(
ALLOWED_PRICE_IMPACT_HIGH
)
&&
!
ackPriceImpact
)
{
return
{
message
:
<
Trans
>
High price impact
</
Trans
>,
onClick
:
()
=>
setAckPriceImpact
(
true
),
children
:
<
Trans
>
Acknowledge
</
Trans
>,
}
}
return
},
[
ackPriceImpact
,
doesTradeDiffer
,
priceImpact
,
trade
])
if
(
!
(
inputAmount
&&
outputAmount
&&
inputCurrency
&&
outputCurrency
))
{
return
null
...
...
@@ -163,11 +178,7 @@ export function SummaryDialog({ trade, allowedSlippage, onConfirm }: SummaryDial
</
Trans
>
)
}
</
Estimate
>
<
ActionButton
onClick=
{
onConfirm
}
onUpdate=
{
()
=>
setConfirmedTrade
(
trade
)
}
update=
{
doesTradeDiffer
?
priceUpdate
:
undefined
}
>
<
ActionButton
onClick=
{
onConfirm
}
action=
{
action
}
>
<
Trans
>
Confirm swap
</
Trans
>
</
ActionButton
>
</
ExpandoColumn
>
...
...
src/lib/components/Swap/SwapButton.tsx
View file @
8404c607
...
...
@@ -95,8 +95,9 @@ export default function SwapButton({ disabled }: SwapButtonProps) {
</
Row
>
</
EtherscanLink
>
),
action
:
<
Trans
>
Approve
</
Trans
>,
icon
:
Spinner
,
onClick
:
addApprovalTransaction
,
children
:
<
Trans
>
Approve
</
Trans
>,
},
}
}
else
if
(
approval
===
ApprovalState
.
NOT_APPROVED
)
{
...
...
@@ -111,7 +112,7 @@ export default function SwapButton({ disabled }: SwapButtonProps) {
}
return
{
disabled
:
true
}
},
[
approval
,
approvalHash
,
chainId
,
disabled
,
inputCurrencyAmount
,
inputCurrencyBalance
])
},
[
a
ddApprovalTransaction
,
a
pproval
,
approvalHash
,
chainId
,
disabled
,
inputCurrencyAmount
,
inputCurrencyBalance
])
const
deadline
=
useTransactionDeadline
()
const
{
signatureData
}
=
useERC20PermitFromTrade
(
optimizedTrade
,
allowedSlippage
,
deadline
)
...
...
@@ -156,7 +157,6 @@ export default function SwapButton({ disabled }: SwapButtonProps) {
<
ActionButton
color=
{
tokenColorExtraction
?
'
interactive
'
:
'
accent
'
}
onClick=
{
()
=>
setActiveTrade
(
trade
.
trade
)
}
onUpdate=
{
addApprovalTransaction
}
{
...
actionProps
}
>
<
Trans
>
Review swap
</
Trans
>
...
...
src/lib/icons/index.tsx
View file @
8404c607
...
...
@@ -12,6 +12,7 @@ import {
ArrowDown
as
ArrowDownIcon
,
ArrowRight
as
ArrowRightIcon
,
ArrowUp
as
ArrowUpIcon
,
BarChart2
as
BarChart2Icon
,
CheckCircle
as
CheckCircleIcon
,
ChevronDown
as
ChevronDownIcon
,
Clock
as
ClockIcon
,
...
...
@@ -75,6 +76,7 @@ export const ArrowDown = icon(ArrowDownIcon)
export
const
ArrowRight
=
icon
(
ArrowRightIcon
)
export
const
ArrowUp
=
icon
(
ArrowUpIcon
)
export
const
CheckCircle
=
icon
(
CheckCircleIcon
)
export
const
BarChart
=
icon
(
BarChart2Icon
)
export
const
ChevronDown
=
icon
(
ChevronDownIcon
)
export
const
Clock
=
icon
(
ClockIcon
)
export
const
HelpCircle
=
icon
(
HelpCircleIcon
)
...
...
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