diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeQuoteResetTimer.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeQuoteResetTimer.tsx
new file mode 100644
index 0000000000..86717650ba
--- /dev/null
+++ b/packages/synapse-interface/components/StateManagedBridge/BridgeQuoteResetTimer.tsx
@@ -0,0 +1,113 @@
+import { useState, useEffect, useMemo } from 'react'
+
+import { BridgeQuote } from '@/utils/types'
+import { convertMsToSeconds } from '@/utils/time'
+
+export const BridgeQuoteResetTimer = ({
+  bridgeQuote,
+  isLoading,
+  isActive,
+  duration, // in ms
+}: {
+  bridgeQuote: BridgeQuote
+  isLoading: boolean
+  isActive: boolean
+  duration: number
+}) => {
+  const memoizedTimer = useMemo(() => {
+    if (!isActive) return null
+
+    if (isLoading) {
+      return <AnimatedLoadingCircle />
+    } else {
+      return (
+        <AnimatedProgressCircle
+          animateKey={bridgeQuote.id}
+          duration={duration}
+        />
+      )
+    }
+  }, [bridgeQuote, duration, isActive])
+
+  return memoizedTimer
+}
+
+const AnimatedLoadingCircle = () => {
+  return (
+    <svg
+      width="24"
+      height="24"
+      viewBox="-12 -12 24 24"
+      stroke="currentcolor"
+      fill="none"
+      className="absolute block -rotate-90"
+    >
+      <circle r="8" pathLength="1" stroke-dashArray="0.05" stroke-opacity=".5">
+        <animate
+          attributeName="stroke-dashoffset"
+          to="-1"
+          dur="2.5s"
+          repeatCount="indefinite"
+        />
+      </circle>
+    </svg>
+  )
+}
+
+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"
+      fill="none"
+      className="absolute block -rotate-90"
+    >
+      <circle r="8" pathLength="1" stroke-opacity=".25">
+        <animate
+          attributeName="stroke-dashoffset"
+          to="-1"
+          dur="2.5s"
+          repeatCount="indefinite"
+        />
+        <set
+          attributeName="stroke-dasharray"
+          to="0.05"
+          begin={`${convertMsToSeconds(duration)}s`}
+        />
+        <set
+          attributeName="stroke-opacity"
+          to="0.5"
+          begin={`${convertMsToSeconds(duration)}s`}
+        />
+      </circle>
+      <circle r="8" stroke-dasharray="1" pathLength="1">
+        <animate
+          attributeName="stroke-dashoffset"
+          values="2; 1"
+          dur={`${convertMsToSeconds(duration)}s`}
+          fill="freeze"
+        />
+        <animate
+          attributeName="stroke-opacity"
+          values="0; 1"
+          dur={`${convertMsToSeconds(duration)}s`}
+        />
+      </circle>
+    </svg>
+  )
+}
diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx
index 44aa9a3fb5..86f6006650 100644
--- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx
+++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx
@@ -12,6 +12,7 @@ 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,
@@ -19,6 +20,7 @@ export const BridgeTransactionButton = ({
   isApproved,
   isBridgePaused,
   isTyping,
+  isQuoteStale,
 }) => {
   const dispatch = useAppDispatch()
   const { openConnectModal } = useConnectModal()
@@ -48,6 +50,8 @@ export const BridgeTransactionButton = ({
     debouncedFromValue,
   } = useBridgeState()
   const { bridgeQuote, isLoading } = useBridgeQuoteState()
+  const { isPendingConfirmChange, onUserAcceptChange } =
+    useConfirmNewBridgePrice()
 
   const { isWalletPending } = useWalletState()
   const { showDestinationWarning, isDestinationWarningAccepted } =
@@ -73,6 +77,7 @@ export const BridgeTransactionButton = ({
     isBridgeQuoteAmountGreaterThanInputForRfq ||
     (isConnected && !hasValidQuote) ||
     (isConnected && !hasSufficientBalance) ||
+    (isConnected && isQuoteStale) ||
     (destinationAddress && !isAddress(destinationAddress))
 
   let buttonProperties
@@ -97,6 +102,26 @@ 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 && hasValidQuote) {
+    buttonProperties = {
+      label: isPendingConfirmChange
+        ? t('Confirm new quote')
+        : t('Bridge {symbol}', { symbol: fromToken?.symbol }),
+      pendingLabel: t('Bridge {symbol}', { symbol: fromToken?.symbol }),
+      onClick: null,
+      className: `
+      ${
+        isPendingConfirmChange
+          ? '!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 }),
@@ -144,11 +169,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'),
@@ -167,6 +187,13 @@ export const BridgeTransactionButton = ({
       onClick: () => switchChain({ chainId: fromChainId }),
       pendingLabel: t('Switching chains'),
     }
+  } else if (isApproved && hasValidQuote && isPendingConfirmChange) {
+    buttonProperties = {
+      label: t('Confirm new quote'),
+      onClick: () => onUserAcceptChange(),
+      className:
+        '!outline !outline-1 !outline-synapsePurple !outline-offset-[-1px] !from-bgLight !to-bgLight',
+    }
   } else if (!isApproved && hasValidInput && hasValidQuote) {
     buttonProperties = {
       onClick: approveTxn,
diff --git a/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx b/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx
index c891391fcd..5af733cb91 100644
--- a/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx
+++ b/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx
@@ -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()
@@ -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">
@@ -48,6 +54,7 @@ export const OutputContainer = () => {
           disabled={true}
           showValue={showValue}
           isLoading={isLoading}
+          className={inputClassName}
         />
       </BridgeAmountContainer>
     </BridgeSectionContainer>
diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useBridgeValidations.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useBridgeValidations.ts
index e64ac72587..b3f31ab0f6 100644
--- a/packages/synapse-interface/components/StateManagedBridge/hooks/useBridgeValidations.ts
+++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useBridgeValidations.ts
@@ -111,7 +111,7 @@ export const useBridgeValidations = () => {
   }
 }
 
-const constructStringifiedBridgeSelections = (
+export const constructStringifiedBridgeSelections = (
   originAmount,
   originChainId,
   originToken,
diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts
new file mode 100644
index 0000000000..2dbead7f24
--- /dev/null
+++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts
@@ -0,0 +1,125 @@
+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 triggerQuoteRef = useRef<BridgeQuote | null>(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]
+  )
+
+  const isPendingConfirmChange =
+    hasQuoteOutputChanged &&
+    hasSameSelectionsAsPreviousQuote &&
+    !hasUserConfirmedChange
+
+  useEffect(() => {
+    const validQuotes =
+      bridgeQuote?.outputAmount && previousBridgeQuote?.outputAmount
+
+    const hasBridgeModuleChanged =
+      bridgeQuote?.bridgeModuleName !==
+      (triggerQuoteRef.current?.bridgeModuleName ??
+        previousBridgeQuote?.bridgeModuleName)
+
+    const outputAmountDiffMoreThanThreshold = validQuotes
+      ? calculateOutputRelativeDifference(
+          bridgeQuote,
+          triggerQuoteRef.current ?? previousBridgeQuote
+        ) > bpsThreshold
+      : false
+
+    if (
+      validQuotes &&
+      hasSameSelectionsAsPreviousQuote &&
+      (outputAmountDiffMoreThanThreshold || hasBridgeModuleChanged)
+    ) {
+      requestUserConfirmChange(previousBridgeQuote)
+    } else {
+      resetConfirm()
+    }
+  }, [bridgeQuote, previousBridgeQuote, hasSameSelectionsAsPreviousQuote])
+
+  const requestUserConfirmChange = (previousQuote: BridgeQuote) => {
+    if (!hasQuoteOutputChanged && !hasUserConfirmedChange) {
+      triggerQuoteRef.current = previousQuote
+      setHasQuoteOutputChanged(true)
+    }
+    setHasUserConfirmedChange(false)
+  }
+
+  const resetConfirm = () => {
+    if (hasUserConfirmedChange) {
+      triggerQuoteRef.current = null
+      setHasQuoteOutputChanged(false)
+      setHasUserConfirmedChange(false)
+    }
+  }
+
+  const onUserAcceptChange = () => {
+    triggerQuoteRef.current = null
+    setHasUserConfirmedChange(true)
+  }
+
+  return {
+    isPendingConfirmChange,
+    onUserAcceptChange,
+  }
+}
+
+const calculateOutputRelativeDifference = (
+  currentQuote?: BridgeQuote,
+  previousQuote?: BridgeQuote
+) => {
+  if (!currentQuote?.outputAmountString || !previousQuote?.outputAmountString) {
+    return null
+  }
+
+  const currentOutput = parseFloat(currentQuote.outputAmountString)
+  const previousOutput = parseFloat(previousQuote.outputAmountString)
+
+  if (previousOutput === 0) {
+    return null
+  }
+
+  return (previousOutput - currentOutput) / previousOutput
+}
diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useStaleQuoteUpdater.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useStaleQuoteUpdater.ts
new file mode 100644
index 0000000000..9a9cbf1444
--- /dev/null
+++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useStaleQuoteUpdater.ts
@@ -0,0 +1,114 @@
+import { useEffect, useRef, useState } from 'react'
+
+import { BridgeQuote } from '@/utils/types'
+import { useIntervalTimer } from '@/utils/hooks/useIntervalTimer'
+
+export const useStaleQuoteUpdater = (
+  quote: BridgeQuote,
+  refreshQuoteCallback: () => Promise<void>,
+  enabled: boolean,
+  staleTimeout: number = 15000, // in ms
+  autoRefreshDuration: number = 30000 // in ms
+) => {
+  const [isStale, setIsStale] = useState<boolean>(false)
+  const autoRefreshIntervalRef = useRef<null | NodeJS.Timeout>(null)
+  const autoRefreshStartTimeRef = useRef<null | number>(null)
+  const mouseMoveListenerRef = useRef<null | (() => void)>(null)
+  const manualRefreshRef = useRef<null | NodeJS.Timeout>(null)
+
+  useIntervalTimer(staleTimeout, !enabled)
+
+  const [mouseMoved, resetMouseMove] = useTrackMouseMove()
+
+  const clearManualRefreshTimeout = () => {
+    if (manualRefreshRef.current) {
+      clearTimeout(manualRefreshRef.current)
+    }
+  }
+
+  const clearAutoRefreshInterval = () => {
+    if (autoRefreshIntervalRef.current) {
+      clearInterval(autoRefreshIntervalRef.current)
+    }
+  }
+
+  const clearMouseMoveListener = () => {
+    if (mouseMoveListenerRef.current) {
+      mouseMoveListenerRef.current = null
+    }
+  }
+
+  useEffect(() => {
+    if (mouseMoved && autoRefreshStartTimeRef.current) {
+      autoRefreshStartTimeRef.current = null
+      resetMouseMove()
+    }
+  }, [quote])
+
+  // Start auto-refresh logic for ${autoRefreshDuration}ms seconds
+  useEffect(() => {
+    if (enabled) {
+      // If auto-refresh has not started yet, initialize the start time
+      if (autoRefreshStartTimeRef.current === null) {
+        autoRefreshStartTimeRef.current = Date.now()
+      }
+
+      const elapsedTime = Date.now() - autoRefreshStartTimeRef.current
+
+      // If ${autoRefreshDuration}ms hasn't passed, keep auto-refreshing
+      if (elapsedTime < autoRefreshDuration) {
+        clearManualRefreshTimeout()
+        clearAutoRefreshInterval()
+
+        autoRefreshIntervalRef.current = setInterval(() => {
+          refreshQuoteCallback()
+        }, staleTimeout)
+      } else {
+        // If more than ${autoRefreshDuration}ms have passed, stop auto-refreshing and switch to mousemove logic
+        clearAutoRefreshInterval()
+
+        manualRefreshRef.current = setTimeout(() => {
+          clearMouseMoveListener()
+          setIsStale(true)
+
+          const handleMouseMove = () => {
+            refreshQuoteCallback()
+            clearMouseMoveListener()
+            setIsStale(false)
+          }
+
+          document.addEventListener('mousemove', handleMouseMove, {
+            once: true,
+          })
+
+          mouseMoveListenerRef.current = handleMouseMove
+        }, staleTimeout)
+      }
+    }
+
+    return () => {
+      clearManualRefreshTimeout()
+      clearAutoRefreshInterval()
+      setIsStale(false)
+    }
+  }, [quote, enabled])
+
+  return isStale
+}
+
+export const useTrackMouseMove = (): [boolean, () => void] => {
+  const [moved, setMoved] = useState<boolean>(false)
+
+  const onMove = () => setMoved(true)
+  const onReset = () => setMoved(false)
+
+  useEffect(() => {
+    document.addEventListener('mousemove', onMove)
+
+    return () => {
+      document.removeEventListener('mousemove', onMove)
+    }
+  }, [])
+
+  return [moved, onReset]
+}
diff --git a/packages/synapse-interface/components/buttons/TransactionButton.tsx b/packages/synapse-interface/components/buttons/TransactionButton.tsx
index e868868dc8..24461d6526 100644
--- a/packages/synapse-interface/components/buttons/TransactionButton.tsx
+++ b/packages/synapse-interface/components/buttons/TransactionButton.tsx
@@ -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 = ({
@@ -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'
diff --git a/packages/synapse-interface/components/ui/AmountInput.tsx b/packages/synapse-interface/components/ui/AmountInput.tsx
index ed352f5176..aebc3fd3a7 100644
--- a/packages/synapse-interface/components/ui/AmountInput.tsx
+++ b/packages/synapse-interface/components/ui/AmountInput.tsx
@@ -11,6 +11,7 @@ interface AmountInputTypes {
   showValue: string
   handleFromValueChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
   setIsTyping?: (isTyping: boolean) => void
+  className?: string
 }
 
 export function AmountInput({
@@ -20,6 +21,7 @@ export function AmountInput({
   showValue,
   handleFromValueChange,
   setIsTyping,
+  className,
 }: AmountInputTypes) {
   const debouncedSetIsTyping = useCallback(
     debounce((value: boolean) => setIsTyping?.(value), 600),
@@ -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 (
diff --git a/packages/synapse-interface/messages/ar.json b/packages/synapse-interface/messages/ar.json
index b52b6f617d..fc6571c49e 100644
--- a/packages/synapse-interface/messages/ar.json
+++ b/packages/synapse-interface/messages/ar.json
@@ -57,7 +57,8 @@
     "ReceiveWithEllipsis": "استلام…",
     "All receivable tokens": "جميع الرموز القابلة للاستلام",
     "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "تم العثور على مسار لجسر {debouncedFromValue} {fromToken} على {fromChainId} إلى {toToken} على {toChainId}",
-    "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "لم يتم العثور على مسار لجسر {debouncedFromValue} {fromToken} على {fromChainId} إلى {toToken} على {toChainId}"
+    "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "لم يتم العثور على مسار لجسر {debouncedFromValue} {fromToken} على {fromChainId} إلى {toToken} على {toChainId}",
+    "Confirm new quote": "تأكيد العرض الجديد"
   },
   "Completed": {
     "to": "إلى",
diff --git a/packages/synapse-interface/messages/en-US.json b/packages/synapse-interface/messages/en-US.json
index 4784ee82d4..7c5a99c5d0 100644
--- a/packages/synapse-interface/messages/en-US.json
+++ b/packages/synapse-interface/messages/en-US.json
@@ -57,7 +57,8 @@
     "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"
   },
   "Completed": {
     "to": "to",
diff --git a/packages/synapse-interface/messages/es.json b/packages/synapse-interface/messages/es.json
index dee3724b09..c6ac15e7d8 100644
--- a/packages/synapse-interface/messages/es.json
+++ b/packages/synapse-interface/messages/es.json
@@ -57,7 +57,8 @@
     "ReceiveWithEllipsis": "Recibir…",
     "All receivable tokens": "Todos los tokens recibibles",
     "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "Ruta encontrada para el puente de {debouncedFromValue} {fromToken} en {fromChainId} a {toToken} en {toChainId}",
-    "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "No se encontró ruta para el puente de {debouncedFromValue} {fromToken} en {fromChainId} a {toToken} en {toChainId}"
+    "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "No se encontró ruta para el puente de {debouncedFromValue} {fromToken} en {fromChainId} a {toToken} en {toChainId}",
+    "Confirm new quote": "Confirmar nueva cotización"
   },
   "Completed": {
     "to": "a",
diff --git a/packages/synapse-interface/messages/fr.json b/packages/synapse-interface/messages/fr.json
index 2541e4c68f..b5f249aaf1 100644
--- a/packages/synapse-interface/messages/fr.json
+++ b/packages/synapse-interface/messages/fr.json
@@ -57,7 +57,8 @@
     "ReceiveWithEllipsis": "Recevoir…",
     "All receivable tokens": "Tous les jetons recevables",
     "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "Route trouvée pour le transfert de {debouncedFromValue} {fromToken} sur {fromChainId} vers {toToken} sur {toChainId}",
-    "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "Aucune route trouvée pour le transfert de {debouncedFromValue} {fromToken} sur {fromChainId} vers {toToken} sur {toChainId}"
+    "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "Aucune route trouvée pour le transfert de {debouncedFromValue} {fromToken} sur {fromChainId} vers {toToken} sur {toChainId}",
+    "Confirm new quote": "Confirmer la nouvelle offre"
   },
   "Completed": {
     "to": "vers",
diff --git a/packages/synapse-interface/messages/jp.json b/packages/synapse-interface/messages/jp.json
index 1d23dd39a9..c187de083b 100644
--- a/packages/synapse-interface/messages/jp.json
+++ b/packages/synapse-interface/messages/jp.json
@@ -57,7 +57,8 @@
     "ReceiveWithEllipsis": "受取…",
     "All receivable tokens": "すべての受取可能トークン",
     "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "{fromChainId}の{debouncedFromValue} {fromToken}から{toChainId}の{toToken}へのブリッジのルートが見つかりました",
-    "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "{fromChainId}の{debouncedFromValue} {fromToken}から{toChainId}の{toToken}へのブリッジのルートが見つかりませんでした"
+    "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "{fromChainId}の{debouncedFromValue} {fromToken}から{toChainId}の{toToken}へのブリッジのルートが見つかりませんでした",
+    "Confirm new quote": "新しい見積もりを確認"
   },
   "Completed": {
     "to": "へ",
diff --git a/packages/synapse-interface/messages/tr.json b/packages/synapse-interface/messages/tr.json
index 34ad00a1f9..dbe44d71b2 100644
--- a/packages/synapse-interface/messages/tr.json
+++ b/packages/synapse-interface/messages/tr.json
@@ -57,7 +57,8 @@
     "ReceiveWithEllipsis": "Al…",
     "All receivable tokens": "Tüm alınabilir tokenler",
     "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "{fromChainId}'deki {debouncedFromValue} {fromToken}'ı {toChainId}'deki {toToken}'a köprüleme için rota bulundu",
-    "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "{fromChainId}'deki {debouncedFromValue} {fromToken}'ı {toChainId}'deki {toToken}'a köprüleme için rota bulunamadı"
+    "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "{fromChainId}'deki {debouncedFromValue} {fromToken}'ı {toChainId}'deki {toToken}'a köprüleme için rota bulunamadı",
+    "Confirm new quote": "Yeni teklifi onayla"
   },
   "Completed": {
     "to": "nereye",
diff --git a/packages/synapse-interface/messages/zh-CN.json b/packages/synapse-interface/messages/zh-CN.json
index 34a2b7500d..0d5cb9e02d 100644
--- a/packages/synapse-interface/messages/zh-CN.json
+++ b/packages/synapse-interface/messages/zh-CN.json
@@ -57,7 +57,8 @@
     "ReceiveWithEllipsis": "接收…",
     "All receivable tokens": "所有可接收代币",
     "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "找到桥接路线:从 {fromChainId} 的 {fromToken} 到 {toChainId} 的 {toToken}",
-    "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "未找到桥接路线:从 {fromChainId} 的 {fromToken} 到 {toChainId} 的 {toToken}"
+    "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "未找到桥接路线:从 {fromChainId} 的 {fromToken} 到 {toChainId} 的 {toToken}",
+    "Confirm new quote": "确认新报价"
   },
   "Completed": {
     "to": "到",
diff --git a/packages/synapse-interface/pages/state-managed-bridge/index.tsx b/packages/synapse-interface/pages/state-managed-bridge/index.tsx
index 87f503de4f..315b0651bd 100644
--- a/packages/synapse-interface/pages/state-managed-bridge/index.tsx
+++ b/packages/synapse-interface/pages/state-managed-bridge/index.tsx
@@ -58,7 +58,6 @@ import { RootState } from '@/store/store'
 import { getUnixTimeMinutesFromNow } from '@/utils/time'
 import { isTransactionReceiptError } from '@/utils/isTransactionReceiptError'
 import { wagmiConfig } from '@/wagmiConfig'
-import { useStaleQuoteUpdater } from '@/utils/hooks/useStaleQuoteUpdater'
 import { useMaintenance } from '@/components/Maintenance/Maintenance'
 import { screenAddress } from '@/utils/screenAddress'
 import { useWalletState } from '@/slices/wallet/hooks'
@@ -66,10 +65,14 @@ 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'
+import { BridgeQuoteResetTimer } from '@/components/StateManagedBridge/BridgeQuoteResetTimer'
+import { useBridgeValidations } from '@/components/StateManagedBridge/hooks/useBridgeValidations'
+import { useStaleQuoteUpdater } from '@/components/StateManagedBridge/hooks/useStaleQuoteUpdater'
 
 const StateManagedBridge = () => {
   const dispatch = useAppDispatch()
-  const { address } = useAccount()
+  const { address, isConnected } = useAccount()
   const { synapseSDK } = useSynapseContext()
   const router = useRouter()
   const { query, pathname } = router
@@ -96,6 +99,8 @@ const StateManagedBridge = () => {
 
   const isApproved = useIsBridgeApproved()
 
+  const { hasValidQuote, hasSufficientBalance } = useBridgeValidations()
+
   const { isWalletPending } = useWalletState()
 
   const { showSettingsSlideOver } = useSelector(
@@ -137,8 +142,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 {
@@ -217,11 +220,18 @@ const StateManagedBridge = () => {
     }
   }
 
-  useStaleQuoteUpdater(
+  const isUpdaterEnabled =
+    isConnected &&
+    hasValidQuote &&
+    hasSufficientBalance &&
+    isApproved &&
+    !isBridgePaused &&
+    !isWalletPending
+
+  const isQuoteStale = useStaleQuoteUpdater(
     bridgeQuote,
     getAndSetBridgeQuote,
-    isLoading,
-    isWalletPending,
+    isUpdaterEnabled,
     quoteTimeout
   )
 
@@ -424,6 +434,10 @@ const StateManagedBridge = () => {
         )
       }
 
+      if (isTransactionUserRejectedError(error)) {
+        getAndSetBridgeQuote()
+      }
+
       return txErrorHandler(error)
     } finally {
       dispatch(setIsWalletPending(false))
@@ -467,18 +481,29 @@ const StateManagedBridge = () => {
                 }}
                 disabled={isWalletPending}
               />
-              <OutputContainer />
+              <OutputContainer isQuoteStale={isQuoteStale} />
               <Warning />
               <BridgeMaintenanceWarningMessage />
               <BridgeExchangeRateInfo />
               <ConfirmDestinationAddressWarning />
-              <BridgeTransactionButton
-                isTyping={isTyping}
-                isApproved={isApproved}
-                approveTxn={approveTxn}
-                executeBridge={executeBridge}
-                isBridgePaused={isBridgePaused}
-              />
+              <div className="relative flex items-center">
+                <BridgeTransactionButton
+                  isTyping={isTyping}
+                  isApproved={isApproved}
+                  approveTxn={approveTxn}
+                  executeBridge={executeBridge}
+                  isBridgePaused={isBridgePaused}
+                  isQuoteStale={isQuoteStale}
+                />
+                <div className="absolute flex items-center !right-10 pointer-events-none">
+                  <BridgeQuoteResetTimer
+                    bridgeQuote={bridgeQuote}
+                    isLoading={isLoading}
+                    isActive={isUpdaterEnabled}
+                    duration={quoteTimeout}
+                  />
+                </div>
+              </div>
             </>
           )}
         </BridgeCard>
diff --git a/packages/synapse-interface/slices/bridgeQuote/reducer.ts b/packages/synapse-interface/slices/bridgeQuote/reducer.ts
index 8407876035..898769f622 100644
--- a/packages/synapse-interface/slices/bridgeQuote/reducer.ts
+++ b/packages/synapse-interface/slices/bridgeQuote/reducer.ts
@@ -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,
 }
 
@@ -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
@@ -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
diff --git a/packages/synapse-interface/store/middleware/bridgeQuoteHistoryMiddleware.ts b/packages/synapse-interface/store/middleware/bridgeQuoteHistoryMiddleware.ts
new file mode 100644
index 0000000000..f58c09ae03
--- /dev/null
+++ b/packages/synapse-interface/store/middleware/bridgeQuoteHistoryMiddleware.ts
@@ -0,0 +1,25 @@
+import {
+  Middleware,
+  MiddlewareAPI,
+  Dispatch,
+  AnyAction,
+} from '@reduxjs/toolkit'
+
+export const bridgeQuoteHistoryMiddleware: Middleware =
+  (store: MiddlewareAPI) => (next: Dispatch) => (action: AnyAction) => {
+    const previousState = store.getState()
+    const result = next(action)
+    const currentState = store.getState()
+
+    if (
+      previousState.bridgeQuote.bridgeQuote !==
+      currentState.bridgeQuote.bridgeQuote
+    ) {
+      store.dispatch({
+        type: 'bridgeQuote/setPreviousBridgeQuote',
+        payload: previousState.bridgeQuote.bridgeQuote,
+      })
+    }
+
+    return result
+  }
diff --git a/packages/synapse-interface/store/destinationAddressMiddleware.ts b/packages/synapse-interface/store/middleware/destinationAddressMiddleware.ts
similarity index 100%
rename from packages/synapse-interface/store/destinationAddressMiddleware.ts
rename to packages/synapse-interface/store/middleware/destinationAddressMiddleware.ts
diff --git a/packages/synapse-interface/store/store.ts b/packages/synapse-interface/store/store.ts
index 70f5515a6f..d8cdf3e70a 100644
--- a/packages/synapse-interface/store/store.ts
+++ b/packages/synapse-interface/store/store.ts
@@ -6,7 +6,8 @@ import { api } from '@/slices/api/slice'
 import { segmentAnalyticsEvent } from '@/contexts/SegmentAnalyticsProvider'
 import { storageKey, persistConfig, persistedReducer } from './reducer'
 import { resetReduxCache } from '@/slices/application/actions'
-import { destinationAddressMiddleware } from '@/store/destinationAddressMiddleware'
+import { destinationAddressMiddleware } from '@/store/middleware/destinationAddressMiddleware'
+import { bridgeQuoteHistoryMiddleware } from './middleware/bridgeQuoteHistoryMiddleware'
 
 const checkVersionAndResetCache = (): boolean => {
   if (typeof window !== 'undefined') {
@@ -28,7 +29,11 @@ export const store = configureStore({
   middleware: (getDefaultMiddleware) =>
     getDefaultMiddleware({
       serializableCheck: false,
-    }).concat(api.middleware, destinationAddressMiddleware),
+    }).concat(
+      api.middleware,
+      destinationAddressMiddleware,
+      bridgeQuoteHistoryMiddleware
+    ),
 })
 
 if (checkVersionAndResetCache()) {
diff --git a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts
deleted file mode 100644
index 05ccd37dc3..0000000000
--- a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { isNull, isNumber } from 'lodash'
-import { useEffect, useRef } from 'react'
-
-import { BridgeQuote } from '@/utils/types'
-import { calculateTimeBetween } from '@/utils/time'
-import { useIntervalTimer } from '@/utils/hooks/useIntervalTimer'
-import { convertUuidToUnix } from '@/utils/convertUuidToUnix'
-
-/**
- * Refreshes quotes based on selected stale timeout duration.
- * Will refresh quote when browser is active and wallet prompt is not pending.
- */
-export const useStaleQuoteUpdater = (
-  quote: BridgeQuote,
-  refreshQuoteCallback: () => Promise<void>,
-  isQuoteLoading: boolean,
-  isWalletPending: boolean,
-  staleTimeout: number = 15000 // Default 15_000ms or 15s
-) => {
-  const eventListenerRef = useRef<null | (() => void)>(null)
-
-  const quoteTime = quote?.id ? convertUuidToUnix(quote?.id) : null
-  const isValidQuote = isNumber(quoteTime) && !isNull(quoteTime)
-
-  const currentTime = useIntervalTimer(staleTimeout, !isValidQuote)
-
-  useEffect(() => {
-    if (isValidQuote && !isQuoteLoading && !isWalletPending) {
-      const timeDifference = calculateTimeBetween(currentTime, quoteTime)
-      const isStaleQuote = timeDifference >= staleTimeout
-
-      if (isStaleQuote) {
-        if (eventListenerRef.current) {
-          document.removeEventListener('mousemove', eventListenerRef.current)
-        }
-
-        const newEventListener = () => {
-          refreshQuoteCallback()
-          eventListenerRef.current = null
-        }
-
-        document.addEventListener('mousemove', newEventListener, {
-          once: true,
-        })
-
-        eventListenerRef.current = newEventListener
-      }
-    }
-  }, [currentTime, staleTimeout])
-}
diff --git a/packages/synapse-interface/utils/time.ts b/packages/synapse-interface/utils/time.ts
index 8e30b5075a..20a8c042f0 100644
--- a/packages/synapse-interface/utils/time.ts
+++ b/packages/synapse-interface/utils/time.ts
@@ -51,3 +51,7 @@ export const isTimestampToday = (unixTimestamp: number): boolean => {
     dateFromTimestamp.getFullYear() === currentDate.getFullYear()
   )
 }
+
+export const convertMsToSeconds = (ms: number) => {
+  return Math.ceil(ms / 1000)
+}