Skip to content

Commit

Permalink
Merge c4701d8 into 2e517b3
Browse files Browse the repository at this point in the history
  • Loading branch information
bigboydiamonds authored Sep 11, 2024
2 parents 2e517b3 + c4701d8 commit 95a4052
Show file tree
Hide file tree
Showing 14 changed files with 398 additions and 51 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { BridgeQuote } from '@/utils/types'
import { useState, useEffect, useMemo } from 'react'

export const BridgeQuoteResetTimer = ({
bridgeQuote,
isActive,
duration, // in ms
}: {
bridgeQuote: BridgeQuote
isActive: boolean
duration: number
}) => {
const memoizedTimer = useMemo(() => {
if (isActive) {
return (
<AnimatedProgressCircle
animateKey={bridgeQuote.id}
duration={duration}
/>
)
}
return null
}, [bridgeQuote, duration, isActive])

return memoizedTimer
}

const AnimatedProgressCircle = ({
animateKey,
duration,
}: {
animateKey: string
duration: number
}) => {
const [animationKey, setAnimationKey] = useState(0)

useEffect(() => {
setAnimationKey((prevKey) => prevKey + 1)
}, [animateKey])

return (
<svg
key={animationKey}
width="24"
height="24"
viewBox="-12 -12 24 24"
stroke="currentcolor"
stroke-opacity=".33"
fill="none"
className="absolute -rotate-90"
>
<circle r="8" />
<circle r="8" stroke-dasharray="1" pathLength="1">
<animate
attributeName="stroke-dashoffset"
values="2; 1"
dur={`${convertMsToSeconds(duration)}s`}
fill="freeze"
/>
</circle>
</svg>
)
}

const convertMsToSeconds = (ms: number) => {
return Math.ceil(ms / 1000)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import { useBridgeDisplayState, useBridgeState } from '@/slices/bridge/hooks'
import { TransactionButton } from '@/components/buttons/TransactionButton'
import { useBridgeValidations } from './hooks/useBridgeValidations'
import { segmentAnalyticsEvent } from '@/contexts/SegmentAnalyticsProvider'
import { useConfirmNewBridgePrice } from './hooks/useConfirmNewBridgePrice'

export const BridgeTransactionButton = ({
approveTxn,
executeBridge,
isApproved,
isBridgePaused,
isTyping,
isQuoteStale,
}) => {
const dispatch = useAppDispatch()
const { openConnectModal } = useConnectModal()
Expand Down Expand Up @@ -48,6 +50,12 @@ export const BridgeTransactionButton = ({
debouncedFromValue,
} = useBridgeState()
const { bridgeQuote, isLoading } = useBridgeQuoteState()
const {
hasSameSelectionsAsPreviousQuote,
hasQuoteOutputChanged,
hasUserConfirmedChange,
onUserAcceptChange,
} = useConfirmNewBridgePrice()

const { isWalletPending } = useWalletState()
const { showDestinationWarning, isDestinationWarningAccepted } =
Expand All @@ -73,6 +81,7 @@ export const BridgeTransactionButton = ({
isBridgeQuoteAmountGreaterThanInputForRfq ||
(isConnected && !hasValidQuote) ||
(isConnected && !hasSufficientBalance) ||
(isConnected && isQuoteStale) ||
(destinationAddress && !isAddress(destinationAddress))

let buttonProperties
Expand All @@ -97,6 +106,23 @@ export const BridgeTransactionButton = ({
label: t('Please select an Origin token'),
onClick: null,
}
} else if (isConnected && !hasSufficientBalance) {
buttonProperties = {
label: t('Insufficient balance'),
onClick: null,
}
} else if (isLoading && hasSameSelectionsAsPreviousQuote) {
buttonProperties = {
label: t('Updating quote'),
onClick: null,
className: `${
hasQuoteOutputChanged &&
hasSameSelectionsAsPreviousQuote &&
!hasUserConfirmedChange
? '!outline !outline-1 !outline-synapsePurple !outline-offset-[-1px] !from-bgLight !to-bgLight'
: '!bg-gradient-to-r !from-fuchsia-500 !to-purple-500 dark:!to-purple-600'
} !opacity-100`,
}
} else if (isLoading) {
buttonProperties = {
label: t('Bridge {symbol}', { symbol: fromToken?.symbol }),
Expand Down Expand Up @@ -144,11 +170,6 @@ export const BridgeTransactionButton = ({
label: t('Invalid bridge quote'),
onClick: null,
}
} else if (!isLoading && isConnected && !hasSufficientBalance) {
buttonProperties = {
label: t('Insufficient balance'),
onClick: null,
}
} else if (destinationAddress && !isAddress(destinationAddress)) {
buttonProperties = {
label: t('Invalid Destination address'),
Expand All @@ -167,6 +188,19 @@ export const BridgeTransactionButton = ({
onClick: () => switchChain({ chainId: fromChainId }),
pendingLabel: t('Switching chains'),
}
} else if (
isApproved &&
hasValidQuote &&
hasQuoteOutputChanged &&
hasSameSelectionsAsPreviousQuote &&
!hasUserConfirmedChange
) {
buttonProperties = {
label: t('Confirm quote'),
onClick: () => onUserAcceptChange(),
className:
'!outline !outline-1 !outline-synapsePurple !outline-offset-[-1px] !from-bgLight !to-bgLight',
}
} else if (!isApproved && hasValidInput && hasValidQuote) {
buttonProperties = {
onClick: approveTxn,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks'
import { useBridgeValidations } from './hooks/useBridgeValidations'
import { useTranslations } from 'next-intl'

export const OutputContainer = () => {
interface OutputContainerProps {
isQuoteStale: boolean
}

export const OutputContainer = ({ isQuoteStale }: OutputContainerProps) => {
const { address } = useAccount()
const { bridgeQuote, isLoading } = useBridgeQuoteState()
const { showDestinationAddress } = useBridgeDisplayState()
Expand All @@ -33,6 +37,8 @@ export const OutputContainer = () => {
}
}, [bridgeQuote, hasValidInput, hasValidQuote])

const inputClassName = isQuoteStale ? 'opacity-50' : undefined

return (
<BridgeSectionContainer>
<div className="flex items-center justify-between">
Expand All @@ -48,6 +54,7 @@ export const OutputContainer = () => {
disabled={true}
showValue={showValue}
isLoading={isLoading}
className={inputClassName}
/>
</BridgeAmountContainer>
</BridgeSectionContainer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export const useBridgeValidations = () => {
}
}

const constructStringifiedBridgeSelections = (
export const constructStringifiedBridgeSelections = (
originAmount,
originChainId,
originToken,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { useState, useEffect, useMemo, useRef } from 'react'

import { useBridgeState } from '@/slices/bridge/hooks'
import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks'
import { constructStringifiedBridgeSelections } from './useBridgeValidations'
import { BridgeQuote } from '@/utils/types'

export const useConfirmNewBridgePrice = () => {
const quoteRef = useRef<any>(null)
const bpsThreshold = 0.0001 // 1bps

const [hasQuoteOutputChanged, setHasQuoteOutputChanged] =
useState<boolean>(false)
const [hasUserConfirmedChange, setHasUserConfirmedChange] =
useState<boolean>(false)

const { bridgeQuote, previousBridgeQuote } = useBridgeQuoteState()
const { debouncedFromValue, fromToken, toToken, fromChainId, toChainId } =
useBridgeState()

const currentBridgeQuoteSelections = useMemo(
() =>
constructStringifiedBridgeSelections(
debouncedFromValue,
fromChainId,
fromToken,
toChainId,
toToken
),
[debouncedFromValue, fromChainId, fromToken, toChainId, toToken]
)

const previousBridgeQuoteSelections = useMemo(
() =>
constructStringifiedBridgeSelections(
previousBridgeQuote?.inputAmountForQuote,
previousBridgeQuote?.originChainId,
previousBridgeQuote?.originTokenForQuote,
previousBridgeQuote?.destChainId,
previousBridgeQuote?.destTokenForQuote
),
[previousBridgeQuote]
)

const hasSameSelectionsAsPreviousQuote = useMemo(
() => currentBridgeQuoteSelections === previousBridgeQuoteSelections,
[currentBridgeQuoteSelections, previousBridgeQuoteSelections]
)

useEffect(() => {
const validQuotes =
bridgeQuote?.outputAmount && previousBridgeQuote?.outputAmount

const outputAmountDiffMoreThanThreshold = validQuotes
? calculateOutputRelativeDifference(
bridgeQuote,
quoteRef.current ?? previousBridgeQuote
) > bpsThreshold
: false

if (
validQuotes &&
outputAmountDiffMoreThanThreshold &&
hasSameSelectionsAsPreviousQuote
) {
requestUserConfirmChange(previousBridgeQuote)
} else {
resetConfirm()
}
}, [bridgeQuote, previousBridgeQuote, hasSameSelectionsAsPreviousQuote])

const requestUserConfirmChange = (previousQuote: BridgeQuote) => {
if (!hasQuoteOutputChanged && !hasUserConfirmedChange) {
quoteRef.current = previousQuote
setHasQuoteOutputChanged(true)
}
setHasUserConfirmedChange(false)
}

const resetConfirm = () => {
if (hasUserConfirmedChange) {
quoteRef.current = null
setHasQuoteOutputChanged(false)
setHasUserConfirmedChange(false)
}
}

const onUserAcceptChange = () => {
quoteRef.current = null
setHasUserConfirmedChange(true)
}

return {
hasSameSelectionsAsPreviousQuote,
hasQuoteOutputChanged,
hasUserConfirmedChange,
onUserAcceptChange,
}
}

const calculateOutputRelativeDifference = (
quoteA?: BridgeQuote,
quoteB?: BridgeQuote
) => {
if (!quoteA?.outputAmountString || !quoteB?.outputAmountString) return null

const outputA = parseFloat(quoteA.outputAmountString)
const outputB = parseFloat(quoteB.outputAmountString)

return Math.abs(outputA - outputB) / outputB
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const baseClassNames = {
disabled: 'disabled:opacity-50 disabled:cursor-not-allowed',
background: 'bg-zinc-400 dark:bg-bgLight',
gradient: 'enabled:bg-gradient-to-r',
transition: 'transition',
}

export const TransactionButton = ({
Expand Down Expand Up @@ -42,8 +43,8 @@ export const TransactionButton = ({
style={style}
disabled={disabled}
className={`
${className}
${joinClassNames(baseClassNames)}
${className}
${
isPending
? 'from-fuchsia-400 dark:from-fuchsia-900 to-purple-400 dark:to-purple-900'
Expand Down
3 changes: 3 additions & 0 deletions packages/synapse-interface/components/ui/AmountInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface AmountInputTypes {
showValue: string
handleFromValueChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
setIsTyping?: (isTyping: boolean) => void
className?: string
}

export function AmountInput({
Expand All @@ -20,6 +21,7 @@ export function AmountInput({
showValue,
handleFromValueChange,
setIsTyping,
className,
}: AmountInputTypes) {
const debouncedSetIsTyping = useCallback(
debounce((value: boolean) => setIsTyping?.(value), 600),
Expand All @@ -38,6 +40,7 @@ export function AmountInput({
placeholder: 'placeholder:text-zinc-500 placeholder:dark:text-zinc-400',
font: 'text-xl md:text-2xl font-medium',
focus: 'focus:outline-none focus:ring-0 focus:border-none',
custom: className,
}

return (
Expand Down
5 changes: 4 additions & 1 deletion packages/synapse-interface/messages/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@
"ReceiveWithEllipsis": "Receive…",
"All receivable tokens": "All receivable tokens",
"Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}",
"No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}"
"No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}",
"Confirm new quote": "Confirm new quote",
"Confirm quote": "Confirm quote",
"Updating quote": "Updating quote"
},
"Completed": {
"to": "to",
Expand Down
Loading

0 comments on commit 95a4052

Please sign in to comment.