Skip to content

Commit

Permalink
Merge f1118b1 into c5ad9f9
Browse files Browse the repository at this point in the history
  • Loading branch information
bigboydiamonds authored Sep 9, 2024
2 parents c5ad9f9 + f1118b1 commit 313cd3a
Show file tree
Hide file tree
Showing 12 changed files with 288 additions and 703 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { BridgeQuote } from '@/utils/types'
import { useState, useEffect } from 'react'

export const BridgeQuoteResetTimer = ({
bridgeQuote,
hasValidQuote,
duration, // in ms
}: {
bridgeQuote: BridgeQuote
hasValidQuote: boolean
duration: number
}) => {
if (hasValidQuote) {
return (
<AnimatedProgressCircle animateKey={bridgeQuote.id} duration={duration} />
)
}
}

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"
strokeOpacity=".33"
fill="none"
className="absolute -rotate-90 -scale-y-100 right-4"
>
<circle r="8" />
<circle r="8" strokeDasharray="1" pathLength="1">
<animate
attributeName="stroke-dashoffset"
values="1; 2"
dur={`${convertMsToSeconds(duration)}s`}
/>
</circle>
</svg>
)
}

const convertMsToSeconds = (ms: number) => {
return Math.ceil(ms / 1000)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,17 @@ 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'
import { BridgeQuoteResetTimer } from './AnimatedProgressCircle'

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

const { isWalletPending } = useWalletState()
const { showDestinationWarning, isDestinationWarningAccepted } =
Expand All @@ -62,6 +72,7 @@ export const BridgeTransactionButton = ({

const isButtonDisabled =
isBridgePaused ||
isQuoteStale ||
isTyping ||
isLoading ||
isWalletPending ||
Expand Down Expand Up @@ -94,6 +105,16 @@ export const BridgeTransactionButton = ({
label: `Please select an Origin token`,
onClick: null,
}
} else if (isConnected && !hasSufficientBalance) {
buttonProperties = {
label: 'Insufficient balance',
onClick: null,
}
} else if (isLoading && hasSameSelectionsAsPreviousQuote) {
buttonProperties = {
label: 'Updating quote',
onClick: null,
}
} else if (isLoading) {
buttonProperties = {
label: `Bridge ${fromToken?.symbol}`,
Expand Down Expand Up @@ -141,11 +162,6 @@ export const BridgeTransactionButton = ({
label: 'Invalid bridge quote',
onClick: null,
}
} else if (!isLoading && isConnected && !hasSufficientBalance) {
buttonProperties = {
label: 'Insufficient balance',
onClick: null,
}
} else if (destinationAddress && !isAddress(destinationAddress)) {
buttonProperties = {
label: 'Invalid Destination address',
Expand All @@ -162,6 +178,13 @@ export const BridgeTransactionButton = ({
onClick: () => switchChain({ chainId: fromChainId }),
pendingLabel: 'Switching chains',
}
} else if (hasQuoteOutputChanged && !hasUserConfirmedChange) {
buttonProperties = {
label: 'Confirm new price',
onClick: () => onUserAcceptChange(),
className:
'!border !border-synapsePurple !from-bgLight !to-bgLight !animate-pulse',
}
} else if (!isApproved && hasValidInput && hasValidQuote) {
buttonProperties = {
onClick: approveTxn,
Expand All @@ -173,6 +196,13 @@ export const BridgeTransactionButton = ({
onClick: executeBridge,
label: `Bridge ${fromToken?.symbol}`,
pendingLabel: 'Bridging',
labelAnimation: (
<BridgeQuoteResetTimer
bridgeQuote={bridgeQuote}
hasValidQuote={hasValidQuote}
duration={quoteTimeout}
/>
),
}
}

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,110 @@
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 [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 outputAmountDiffMoreThan1bps = validQuotes
? calculateOutputRelativeDifference(
bridgeQuote,
quoteRef.current ?? previousBridgeQuote
) > 0.0001
: false

if (
validQuotes &&
outputAmountDiffMoreThan1bps &&
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 @@ -19,6 +19,7 @@ export const TransactionButton = ({
onClick,
pendingLabel,
label,
labelAnimation,
onSuccess,
disabled,
chainId,
Expand All @@ -29,6 +30,7 @@ export const TransactionButton = ({
onClick: () => Promise<TransactionResponse | any>
pendingLabel: string
label: string
labelAnimation?: React.ReactNode
onSuccess?: () => void
chainId?: number
style?: CSSProperties
Expand Down Expand Up @@ -63,7 +65,9 @@ export const TransactionButton = ({
<span className="opacity-30">{pendingLabel}</span>{' '}
</>
) : (
<>{label}</>
<>
{label} {labelAnimation}
</>
)}
</Button>
)
Expand Down
11 changes: 8 additions & 3 deletions packages/synapse-interface/pages/state-managed-bridge/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks'
import { resetBridgeQuote } from '@/slices/bridgeQuote/reducer'
import { fetchBridgeQuote } from '@/slices/bridgeQuote/thunks'
import { useIsBridgeApproved } from '@/utils/hooks/useIsBridgeApproved'
import { isTransactionUserRejectedError } from '@/utils/isTransactionUserRejectedError'

const StateManagedBridge = () => {
const dispatch = useAppDispatch()
Expand Down Expand Up @@ -136,8 +137,6 @@ const StateManagedBridge = () => {

// will have to handle deadlineMinutes here at later time, gets passed as optional last arg in .bridgeQuote()

/* clear stored bridge quote before requesting new bridge quote */
dispatch(resetBridgeQuote())
const currentTimestamp: number = getUnixTimeMinutesFromNow(0)

try {
Expand Down Expand Up @@ -198,7 +197,7 @@ const StateManagedBridge = () => {
}
}

useStaleQuoteUpdater(
const isStale = useStaleQuoteUpdater(
bridgeQuote,
getAndSetBridgeQuote,
isLoading,
Expand Down Expand Up @@ -398,6 +397,10 @@ const StateManagedBridge = () => {
)
}

if (isTransactionUserRejectedError) {
getAndSetBridgeQuote()
}

return txErrorHandler(error)
} finally {
dispatch(setIsWalletPending(false))
Expand Down Expand Up @@ -452,6 +455,8 @@ const StateManagedBridge = () => {
approveTxn={approveTxn}
executeBridge={executeBridge}
isBridgePaused={isBridgePaused}
isQuoteStale={isStale}
quoteTimeout={quoteTimeout}
/>
</>
)}
Expand Down
8 changes: 7 additions & 1 deletion packages/synapse-interface/slices/bridgeQuote/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import { fetchBridgeQuote } from './thunks'

export interface BridgeQuoteState {
bridgeQuote: BridgeQuote
previousBridgeQuote: BridgeQuote | null
isLoading: boolean
}

export const initialState: BridgeQuoteState = {
bridgeQuote: EMPTY_BRIDGE_QUOTE,
previousBridgeQuote: null,
isLoading: false,
}

Expand All @@ -24,6 +26,9 @@ export const bridgeQuoteSlice = createSlice({
resetBridgeQuote: (state) => {
state.bridgeQuote = initialState.bridgeQuote
},
setPreviousBridgeQuote: (state, action: PayloadAction<any>) => {
state.previousBridgeQuote = action.payload
},
},
extraReducers: (builder) => {
builder
Expand All @@ -44,6 +49,7 @@ export const bridgeQuoteSlice = createSlice({
},
})

export const { resetBridgeQuote, setIsLoading } = bridgeQuoteSlice.actions
export const { resetBridgeQuote, setIsLoading, setPreviousBridgeQuote } =
bridgeQuoteSlice.actions

export default bridgeQuoteSlice.reducer
Loading

0 comments on commit 313cd3a

Please sign in to comment.