From 38dec50c9f1aba262e2a07ae99b300e07b74d051 Mon Sep 17 00:00:00 2001 From: abtestingalpha Date: Thu, 29 Aug 2024 13:04:25 -0400 Subject: [PATCH 01/31] bridge quote history middleware --- .../slices/bridgeQuote/reducer.ts | 8 +++++- .../bridgeQuoteHistoryMiddleware.ts | 25 +++++++++++++++++++ .../destinationAddressMiddleware.ts | 0 packages/synapse-interface/store/store.ts | 9 +++++-- 4 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 packages/synapse-interface/store/middleware/bridgeQuoteHistoryMiddleware.ts rename packages/synapse-interface/store/{ => middleware}/destinationAddressMiddleware.ts (100%) 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) => { + 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()) { From f44b62c7f80a4fe01693831f50d22d349653ed20 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Fri, 30 Aug 2024 11:04:06 -0700 Subject: [PATCH 02/31] skip resetting quote at beginning of fetch --- packages/synapse-interface/pages/state-managed-bridge/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/synapse-interface/pages/state-managed-bridge/index.tsx b/packages/synapse-interface/pages/state-managed-bridge/index.tsx index 63b7b71e42..997b02b38a 100644 --- a/packages/synapse-interface/pages/state-managed-bridge/index.tsx +++ b/packages/synapse-interface/pages/state-managed-bridge/index.tsx @@ -134,8 +134,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 = getTimeMinutesFromNow(0) try { From 3e388696d3277b3e698fe677f6ed8a6efaf3d941 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Fri, 30 Aug 2024 12:15:04 -0700 Subject: [PATCH 03/31] bridge button requests user to confirm new bridge price when detected --- .vscode/settings.json | 2 +- .../BridgeTransactionButton.tsx | 17 ++++- .../hooks/useBridgeValidations.ts | 2 +- .../hooks/useConfirmNewBridgePrice.ts | 62 +++++++++++++++++++ 4 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index ed3d2aeafe..ecf06d181a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -22,4 +22,4 @@ "solidity.compileUsingRemoteVersion": "v0.8.17+commit.8df45f5f", "solidity.formatter": "prettier", "solidity.defaultCompiler": "remote" -} +} \ No newline at end of file diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx index 48c0aa19ec..6f36ba148c 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx @@ -9,8 +9,12 @@ import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks' import { setIsDestinationWarningAccepted } from '@/slices/bridgeDisplaySlice' import { useBridgeDisplayState, useBridgeState } from '@/slices/bridge/hooks' import { TransactionButton } from '@/components/buttons/TransactionButton' -import { useBridgeValidations } from './hooks/useBridgeValidations' +import { + useBridgeValidations, + constructStringifiedBridgeSelections, +} from './hooks/useBridgeValidations' import { segmentAnalyticsEvent } from '@/contexts/SegmentAnalyticsProvider' +import { useConfirmNewBridgePrice } from './hooks/useConfirmNewBridgePrice' export const BridgeTransactionButton = ({ approveTxn, @@ -45,6 +49,11 @@ export const BridgeTransactionButton = ({ debouncedFromValue, } = useBridgeState() const { bridgeQuote, isLoading } = useBridgeQuoteState() + const { + hasQuoteOutputChanged, + hasUserConfirmedChange, + setHasUserConfirmedChange, + } = useConfirmNewBridgePrice() const { isWalletPending } = useWalletState() const { showDestinationWarning, isDestinationWarningAccepted } = @@ -161,6 +170,12 @@ export const BridgeTransactionButton = ({ onClick: () => switchChain({ chainId: fromChainId }), pendingLabel: 'Switching chains', } + } else if (hasQuoteOutputChanged && !hasUserConfirmedChange) { + buttonProperties = { + label: 'Confirm new price', + onClick: () => setHasUserConfirmedChange(true), + className: 'border-synapsePurple !from-bgLight !to-bgLight', + } } else if (!isApproved && hasValidInput && hasValidQuote) { buttonProperties = { onClick: approveTxn, 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..a67e3789fd --- /dev/null +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts @@ -0,0 +1,62 @@ +import { useState, useEffect, useMemo } from 'react' + +import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks' +import { constructStringifiedBridgeSelections } from './useBridgeValidations' + +export const useConfirmNewBridgePrice = () => { + const [hasQuoteOutputChanged, setHasQuoteOutputChanged] = + useState(false) + const [hasUserConfirmedChange, setHasUserConfirmedChange] = + useState(false) + + const { bridgeQuote, previousBridgeQuote } = useBridgeQuoteState() + + const currentBridgeQuoteSelections = useMemo( + () => + constructStringifiedBridgeSelections( + bridgeQuote?.inputAmountForQuote, + bridgeQuote?.originChainId, + bridgeQuote?.originTokenForQuote, + bridgeQuote?.destChainId, + bridgeQuote?.destTokenForQuote + ), + [bridgeQuote] + ) + + const previousBridgeQuoteSelections = useMemo( + () => + constructStringifiedBridgeSelections( + previousBridgeQuote?.inputAmountForQuote, + previousBridgeQuote?.originChainId, + previousBridgeQuote?.originTokenForQuote, + previousBridgeQuote?.destChainId, + previousBridgeQuote?.destTokenForQuote + ), + [previousBridgeQuote] + ) + + useEffect(() => { + const selectionsMatch = + currentBridgeQuoteSelections === previousBridgeQuoteSelections + + const outputAmountChanged = + bridgeQuote?.outputAmount !== previousBridgeQuote?.outputAmount + + setHasQuoteOutputChanged(selectionsMatch && outputAmountChanged) + + if (!selectionsMatch || !outputAmountChanged) { + setHasUserConfirmedChange(false) + } + }, [ + bridgeQuote, + previousBridgeQuote, + currentBridgeQuoteSelections, + previousBridgeQuoteSelections, + ]) + + return { + hasQuoteOutputChanged, + hasUserConfirmedChange, + setHasUserConfirmedChange, + } +} From bd29349e8b2db8b3a6a6a5c1236310eba31ea47f Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Fri, 30 Aug 2024 12:27:21 -0700 Subject: [PATCH 04/31] conditions for displaying confirm price --- .../StateManagedBridge/BridgeTransactionButton.tsx | 2 +- .../StateManagedBridge/hooks/useConfirmNewBridgePrice.ts | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx index 6f36ba148c..2f2d26e9be 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx @@ -174,7 +174,7 @@ export const BridgeTransactionButton = ({ buttonProperties = { label: 'Confirm new price', onClick: () => setHasUserConfirmedChange(true), - className: 'border-synapsePurple !from-bgLight !to-bgLight', + className: '!border !border-synapsePurple !from-bgLight !to-bgLight', } } else if (!isApproved && hasValidInput && hasValidQuote) { buttonProperties = { diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts index a67e3789fd..3e234663e0 100644 --- a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts @@ -36,15 +36,20 @@ export const useConfirmNewBridgePrice = () => { ) useEffect(() => { + const isValidQuotes = + bridgeQuote?.outputAmount && previousBridgeQuote?.outputAmount + const selectionsMatch = currentBridgeQuoteSelections === previousBridgeQuoteSelections const outputAmountChanged = bridgeQuote?.outputAmount !== previousBridgeQuote?.outputAmount - setHasQuoteOutputChanged(selectionsMatch && outputAmountChanged) + setHasQuoteOutputChanged( + isValidQuotes && selectionsMatch && outputAmountChanged + ) - if (!selectionsMatch || !outputAmountChanged) { + if (outputAmountChanged || !selectionsMatch) { setHasUserConfirmedChange(false) } }, [ From 76663ba15355852e1a04112650fd91cfd13e65d1 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Fri, 30 Aug 2024 13:00:57 -0700 Subject: [PATCH 05/31] confirm prices based on initial triggered ref --- .../BridgeTransactionButton.tsx | 3 ++- .../hooks/useConfirmNewBridgePrice.ts | 23 +++++++++++++------ .../components/buttons/TransactionButton.tsx | 2 +- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx index 2f2d26e9be..f2d5fcf039 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx @@ -174,7 +174,8 @@ export const BridgeTransactionButton = ({ buttonProperties = { label: 'Confirm new price', onClick: () => setHasUserConfirmedChange(true), - className: '!border !border-synapsePurple !from-bgLight !to-bgLight', + className: + '!border !border-synapsePurple !from-bgLight !to-bgLight !animate-pulse !hover:animate-none', } } else if (!isApproved && hasValidInput && hasValidQuote) { buttonProperties = { diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts index 3e234663e0..b071e87723 100644 --- a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts @@ -1,4 +1,4 @@ -import { useState, useEffect, useMemo } from 'react' +import { useState, useEffect, useMemo, useRef } from 'react' import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks' import { constructStringifiedBridgeSelections } from './useBridgeValidations' @@ -9,6 +9,8 @@ export const useConfirmNewBridgePrice = () => { const [hasUserConfirmedChange, setHasUserConfirmedChange] = useState(false) + const triggeredQuoteRef = useRef(null) + const { bridgeQuote, previousBridgeQuote } = useBridgeQuoteState() const currentBridgeQuoteSelections = useMemo( @@ -36,7 +38,7 @@ export const useConfirmNewBridgePrice = () => { ) useEffect(() => { - const isValidQuotes = + const validQuotes = bridgeQuote?.outputAmount && previousBridgeQuote?.outputAmount const selectionsMatch = @@ -45,12 +47,19 @@ export const useConfirmNewBridgePrice = () => { const outputAmountChanged = bridgeQuote?.outputAmount !== previousBridgeQuote?.outputAmount - setHasQuoteOutputChanged( - isValidQuotes && selectionsMatch && outputAmountChanged - ) - - if (outputAmountChanged || !selectionsMatch) { + if (validQuotes && selectionsMatch && outputAmountChanged) { + // Ref quote that triggered the change + triggeredQuoteRef.current = bridgeQuote + setHasQuoteOutputChanged(true) setHasUserConfirmedChange(false) + } else if ( + bridgeQuote?.outputAmount === triggeredQuoteRef?.current?.outputAmount && + selectionsMatch + ) { + // Maintain status until User confirms ref changes + setHasQuoteOutputChanged(true) + } else { + setHasQuoteOutputChanged(false) } }, [ bridgeQuote, diff --git a/packages/synapse-interface/components/buttons/TransactionButton.tsx b/packages/synapse-interface/components/buttons/TransactionButton.tsx index 8decf4bb50..6cb70ce902 100644 --- a/packages/synapse-interface/components/buttons/TransactionButton.tsx +++ b/packages/synapse-interface/components/buttons/TransactionButton.tsx @@ -42,8 +42,8 @@ export const TransactionButton = ({ style={style} disabled={disabled} className={` - ${className} ${BASE_PROPERTIES} + ${className} ${ isPending ? 'from-fuchsia-400 dark:from-fuchsia-900 to-purple-400 dark:to-purple-900' From 18c817681e2821ce7e087e74851689b5b81b1cb3 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Fri, 30 Aug 2024 13:05:03 -0700 Subject: [PATCH 06/31] nit --- .../StateManagedBridge/BridgeTransactionButton.tsx | 2 +- .../StateManagedBridge/hooks/useConfirmNewBridgePrice.ts | 8 ++++---- .../components/buttons/TransactionButton.tsx | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx index f2d5fcf039..fc675484fa 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx @@ -175,7 +175,7 @@ export const BridgeTransactionButton = ({ label: 'Confirm new price', onClick: () => setHasUserConfirmedChange(true), className: - '!border !border-synapsePurple !from-bgLight !to-bgLight !animate-pulse !hover:animate-none', + '!border !border-synapsePurple !from-bgLight !to-bgLight !animate-pulse', } } else if (!isApproved && hasValidInput && hasValidQuote) { buttonProperties = { diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts index b071e87723..ffc3bfc3fb 100644 --- a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts @@ -47,16 +47,16 @@ export const useConfirmNewBridgePrice = () => { const outputAmountChanged = bridgeQuote?.outputAmount !== previousBridgeQuote?.outputAmount - if (validQuotes && selectionsMatch && outputAmountChanged) { + if (selectionsMatch && validQuotes && outputAmountChanged) { // Ref quote that triggered the change triggeredQuoteRef.current = bridgeQuote setHasQuoteOutputChanged(true) setHasUserConfirmedChange(false) } else if ( - bridgeQuote?.outputAmount === triggeredQuoteRef?.current?.outputAmount && - selectionsMatch + selectionsMatch && + bridgeQuote?.outputAmount === triggeredQuoteRef?.current?.outputAmount ) { - // Maintain status until User confirms ref changes + // Maintain status until User confirms ref quote update setHasQuoteOutputChanged(true) } else { setHasQuoteOutputChanged(false) diff --git a/packages/synapse-interface/components/buttons/TransactionButton.tsx b/packages/synapse-interface/components/buttons/TransactionButton.tsx index 6cb70ce902..8decf4bb50 100644 --- a/packages/synapse-interface/components/buttons/TransactionButton.tsx +++ b/packages/synapse-interface/components/buttons/TransactionButton.tsx @@ -42,8 +42,8 @@ export const TransactionButton = ({ style={style} disabled={disabled} className={` - ${BASE_PROPERTIES} ${className} + ${BASE_PROPERTIES} ${ isPending ? 'from-fuchsia-400 dark:from-fuchsia-900 to-purple-400 dark:to-purple-900' From 6d14aca1463532aa7f8cd93d34c2f65062156ef6 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Tue, 3 Sep 2024 14:40:01 -0700 Subject: [PATCH 07/31] request User confirm price if change greater than 1 bps --- .../hooks/useConfirmNewBridgePrice.ts | 42 ++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts index ffc3bfc3fb..ac20572b27 100644 --- a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts @@ -9,7 +9,7 @@ export const useConfirmNewBridgePrice = () => { const [hasUserConfirmedChange, setHasUserConfirmedChange] = useState(false) - const triggeredQuoteRef = useRef(null) + const quoteRef = useRef(null) const { bridgeQuote, previousBridgeQuote } = useBridgeQuoteState() @@ -47,18 +47,50 @@ export const useConfirmNewBridgePrice = () => { const outputAmountChanged = bridgeQuote?.outputAmount !== previousBridgeQuote?.outputAmount - if (selectionsMatch && validQuotes && outputAmountChanged) { - // Ref quote that triggered the change - triggeredQuoteRef.current = bridgeQuote + const outputAmountDiffMoreThan1bps = + validQuotes && quoteRef?.current?.outputAmountString + ? Math.abs( + parseFloat(bridgeQuote?.outputAmountString) - + parseFloat(quoteRef?.current?.outputAmountString) + ) / + parseFloat(quoteRef?.current?.outputAmountString) > + 0.0001 + : validQuotes + ? Math.abs( + parseFloat(bridgeQuote?.outputAmountString) - + parseFloat(previousBridgeQuote?.outputAmountString) + ) / + parseFloat(previousBridgeQuote?.outputAmountString) > + 0.0001 + : false + + // console.log('outputAmountDiffMoreThan1bps:', outputAmountDiffMoreThan1bps) + // console.log( + // 'bridgeQuote?.outputAmountString: ', + // bridgeQuote?.outputAmountString + // ) + // console.log( + // 'previousBridgeQuote?.outputAmountString: ', + // previousBridgeQuote?.outputAmountString + // ) + + if ( + validQuotes && + selectionsMatch && + outputAmountChanged && + outputAmountDiffMoreThan1bps + ) { + quoteRef.current = bridgeQuote setHasQuoteOutputChanged(true) setHasUserConfirmedChange(false) } else if ( selectionsMatch && - bridgeQuote?.outputAmount === triggeredQuoteRef?.current?.outputAmount + bridgeQuote?.outputAmount === quoteRef?.current?.outputAmount ) { // Maintain status until User confirms ref quote update setHasQuoteOutputChanged(true) } else { + quoteRef.current = null setHasQuoteOutputChanged(false) } }, [ From cd75683ab44c41d1ff1b1ffc47be0124fe903e3b Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:15:34 -0700 Subject: [PATCH 08/31] callback functions to handle creating/accepting/reset confirm flow --- .../BridgeTransactionButton.tsx | 4 +- .../hooks/useConfirmNewBridgePrice.ts | 147 ++++++++++-------- 2 files changed, 80 insertions(+), 71 deletions(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx index c9cdea2d09..c49db357d9 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx @@ -52,7 +52,7 @@ export const BridgeTransactionButton = ({ const { hasQuoteOutputChanged, hasUserConfirmedChange, - setHasUserConfirmedChange, + handleUserAcceptChange, } = useConfirmNewBridgePrice() const { isWalletPending } = useWalletState() @@ -174,7 +174,7 @@ export const BridgeTransactionButton = ({ } else if (hasQuoteOutputChanged && !hasUserConfirmedChange) { buttonProperties = { label: 'Confirm new price', - onClick: () => setHasUserConfirmedChange(true), + onClick: () => handleUserAcceptChange(), className: '!border !border-synapsePurple !from-bgLight !to-bgLight !animate-pulse', } diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts index ac20572b27..4d6d032167 100644 --- a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts @@ -1,108 +1,117 @@ -import { useState, useEffect, useMemo, useRef } from 'react' +import { useState, useEffect, useMemo, useRef, useCallback } from 'react' import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks' import { constructStringifiedBridgeSelections } from './useBridgeValidations' export const useConfirmNewBridgePrice = () => { + const quoteRef = useRef(null) + const [hasQuoteOutputChanged, setHasQuoteOutputChanged] = useState(false) const [hasUserConfirmedChange, setHasUserConfirmedChange] = useState(false) - const quoteRef = useRef(null) - const { bridgeQuote, previousBridgeQuote } = useBridgeQuoteState() - const currentBridgeQuoteSelections = useMemo( - () => + const createBridgeSelections = useCallback( + (quote) => constructStringifiedBridgeSelections( - bridgeQuote?.inputAmountForQuote, - bridgeQuote?.originChainId, - bridgeQuote?.originTokenForQuote, - bridgeQuote?.destChainId, - bridgeQuote?.destTokenForQuote + quote?.inputAmountForQuote, + quote?.originChainId, + quote?.originTokenForQuote, + quote?.destChainId, + quote?.destTokenForQuote ), - [bridgeQuote] + [] + ) + + const currentBridgeQuoteSelections = useMemo( + () => createBridgeSelections(bridgeQuote), + [bridgeQuote, createBridgeSelections] ) const previousBridgeQuoteSelections = useMemo( - () => - constructStringifiedBridgeSelections( - previousBridgeQuote?.inputAmountForQuote, - previousBridgeQuote?.originChainId, - previousBridgeQuote?.originTokenForQuote, - previousBridgeQuote?.destChainId, - previousBridgeQuote?.destTokenForQuote - ), - [previousBridgeQuote] + () => createBridgeSelections(previousBridgeQuote), + [previousBridgeQuote, createBridgeSelections] ) + const calculateOutputRelativeDifference = useCallback((quoteA, quoteB) => { + if (!quoteA?.outputAmountString || !quoteB?.outputAmountString) return null + + const outputA = parseFloat(quoteA.outputAmountString) + const outputB = parseFloat(quoteB.outputAmountString) + + return Math.abs(outputA - outputB) / outputB + }, []) + + const handleRequestUserConfirmChange = (previousQuote) => { + if (!hasQuoteOutputChanged && !hasUserConfirmedChange) { + quoteRef.current = previousQuote + setHasQuoteOutputChanged(true) + setHasUserConfirmedChange(false) + } + } + + const handleUserAcceptChange = () => { + quoteRef.current = null + setHasUserConfirmedChange(true) + } + + const handleReset = () => { + if (hasUserConfirmedChange) { + quoteRef.current = null + setHasQuoteOutputChanged(false) + setHasUserConfirmedChange(false) + } + } + useEffect(() => { const validQuotes = bridgeQuote?.outputAmount && previousBridgeQuote?.outputAmount - const selectionsMatch = currentBridgeQuoteSelections === previousBridgeQuoteSelections - const outputAmountChanged = - bridgeQuote?.outputAmount !== previousBridgeQuote?.outputAmount - - const outputAmountDiffMoreThan1bps = - validQuotes && quoteRef?.current?.outputAmountString - ? Math.abs( - parseFloat(bridgeQuote?.outputAmountString) - - parseFloat(quoteRef?.current?.outputAmountString) - ) / - parseFloat(quoteRef?.current?.outputAmountString) > - 0.0001 - : validQuotes - ? Math.abs( - parseFloat(bridgeQuote?.outputAmountString) - - parseFloat(previousBridgeQuote?.outputAmountString) - ) / - parseFloat(previousBridgeQuote?.outputAmountString) > - 0.0001 - : false - - // console.log('outputAmountDiffMoreThan1bps:', outputAmountDiffMoreThan1bps) - // console.log( - // 'bridgeQuote?.outputAmountString: ', - // bridgeQuote?.outputAmountString - // ) - // console.log( - // 'previousBridgeQuote?.outputAmountString: ', - // previousBridgeQuote?.outputAmountString - // ) - - if ( - validQuotes && - selectionsMatch && - outputAmountChanged && - outputAmountDiffMoreThan1bps - ) { - quoteRef.current = bridgeQuote - setHasQuoteOutputChanged(true) - setHasUserConfirmedChange(false) - } else if ( - selectionsMatch && - bridgeQuote?.outputAmount === quoteRef?.current?.outputAmount - ) { - // Maintain status until User confirms ref quote update - setHasQuoteOutputChanged(true) + const outputAmountDiffMoreThan1bps = validQuotes + ? calculateOutputRelativeDifference( + bridgeQuote, + quoteRef.current ?? previousBridgeQuote + ) > 0.0001 + : false + + console.log('quoteRef.current:', quoteRef.current?.outputAmountString) + console.log( + 'bridgeQuote?.outputAmountString: ', + bridgeQuote?.outputAmountString + ) + console.log( + 'previousBridgeQuote?.outputAmountString:', + previousBridgeQuote?.outputAmountString + ) + console.log( + 'relative difference: ', + calculateOutputRelativeDifference( + bridgeQuote, + quoteRef.current ?? previousBridgeQuote + ) + ) + console.log('outputAmountDiffMoreThan1bps: ', outputAmountDiffMoreThan1bps) + + if (validQuotes && selectionsMatch && outputAmountDiffMoreThan1bps) { + handleRequestUserConfirmChange(previousBridgeQuote) } else { - quoteRef.current = null - setHasQuoteOutputChanged(false) + handleReset() } }, [ bridgeQuote, previousBridgeQuote, currentBridgeQuoteSelections, previousBridgeQuoteSelections, + calculateOutputRelativeDifference, ]) return { hasQuoteOutputChanged, hasUserConfirmedChange, - setHasUserConfirmedChange, + handleUserAcceptChange, } } From febd306eddaeebb148ec839f908a33b337b7ec67 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Wed, 4 Sep 2024 16:07:55 -0700 Subject: [PATCH 09/31] request user confirm after first accepted --- .../StateManagedBridge/hooks/useConfirmNewBridgePrice.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts index 4d6d032167..c6ab7cfed3 100644 --- a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts @@ -48,8 +48,8 @@ export const useConfirmNewBridgePrice = () => { if (!hasQuoteOutputChanged && !hasUserConfirmedChange) { quoteRef.current = previousQuote setHasQuoteOutputChanged(true) - setHasUserConfirmedChange(false) } + setHasUserConfirmedChange(false) } const handleUserAcceptChange = () => { From e7df2386edbc3595a862dd69186bc86c3a11e552 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:33:57 -0700 Subject: [PATCH 10/31] fe/updating-quote (#3104) * request user confirm after first accepted * Update button message to reflect when quote updating * change button text ordering for insufficient balance * rm log --- .../BridgeTransactionButton.tsx | 16 +++++++++++----- .../hooks/useConfirmNewBridgePrice.ts | 8 +++++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx index c49db357d9..c2a87dac84 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx @@ -50,6 +50,7 @@ export const BridgeTransactionButton = ({ } = useBridgeState() const { bridgeQuote, isLoading } = useBridgeQuoteState() const { + hasSameSelectionsAsPreviousQuote, hasQuoteOutputChanged, hasUserConfirmedChange, handleUserAcceptChange, @@ -103,6 +104,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}`, @@ -150,11 +161,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', diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts index 4d6d032167..568c94acb7 100644 --- a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts @@ -35,6 +35,11 @@ export const useConfirmNewBridgePrice = () => { [previousBridgeQuote, createBridgeSelections] ) + const hasSameSelectionsAsPreviousQuote = useMemo( + () => currentBridgeQuoteSelections === previousBridgeQuoteSelections, + [currentBridgeQuoteSelections, previousBridgeQuoteSelections] + ) + const calculateOutputRelativeDifference = useCallback((quoteA, quoteB) => { if (!quoteA?.outputAmountString || !quoteB?.outputAmountString) return null @@ -48,8 +53,8 @@ export const useConfirmNewBridgePrice = () => { if (!hasQuoteOutputChanged && !hasUserConfirmedChange) { quoteRef.current = previousQuote setHasQuoteOutputChanged(true) - setHasUserConfirmedChange(false) } + setHasUserConfirmedChange(false) } const handleUserAcceptChange = () => { @@ -110,6 +115,7 @@ export const useConfirmNewBridgePrice = () => { ]) return { + hasSameSelectionsAsPreviousQuote, hasQuoteOutputChanged, hasUserConfirmedChange, handleUserAcceptChange, From 571b39101cf567e21a587f1ab551e1354e36d292 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:53:02 -0700 Subject: [PATCH 11/31] clean logic --- .../BridgeTransactionButton.tsx | 4 +- .../hooks/useConfirmNewBridgePrice.ts | 133 ++++++++---------- 2 files changed, 62 insertions(+), 75 deletions(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx index c2a87dac84..975d139e27 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx @@ -53,7 +53,7 @@ export const BridgeTransactionButton = ({ hasSameSelectionsAsPreviousQuote, hasQuoteOutputChanged, hasUserConfirmedChange, - handleUserAcceptChange, + onUserAcceptChange, } = useConfirmNewBridgePrice() const { isWalletPending } = useWalletState() @@ -180,7 +180,7 @@ export const BridgeTransactionButton = ({ } else if (hasQuoteOutputChanged && !hasUserConfirmedChange) { buttonProperties = { label: 'Confirm new price', - onClick: () => handleUserAcceptChange(), + onClick: () => onUserAcceptChange(), className: '!border !border-synapsePurple !from-bgLight !to-bgLight !animate-pulse', } diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts index 568c94acb7..2f4a43ab74 100644 --- a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts @@ -1,7 +1,9 @@ -import { useState, useEffect, useMemo, useRef, useCallback } from 'react' +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(null) @@ -12,27 +14,31 @@ export const useConfirmNewBridgePrice = () => { useState(false) const { bridgeQuote, previousBridgeQuote } = useBridgeQuoteState() + const { debouncedFromValue, fromToken, toToken, fromChainId, toChainId } = + useBridgeState() - const createBridgeSelections = useCallback( - (quote) => + const currentBridgeQuoteSelections = useMemo( + () => constructStringifiedBridgeSelections( - quote?.inputAmountForQuote, - quote?.originChainId, - quote?.originTokenForQuote, - quote?.destChainId, - quote?.destTokenForQuote + debouncedFromValue, + fromChainId, + fromToken, + toChainId, + toToken ), - [] - ) - - const currentBridgeQuoteSelections = useMemo( - () => createBridgeSelections(bridgeQuote), - [bridgeQuote, createBridgeSelections] + [debouncedFromValue, fromChainId, fromToken, toChainId, toToken] ) const previousBridgeQuoteSelections = useMemo( - () => createBridgeSelections(previousBridgeQuote), - [previousBridgeQuote, createBridgeSelections] + () => + constructStringifiedBridgeSelections( + previousBridgeQuote?.inputAmountForQuote, + previousBridgeQuote?.originChainId, + previousBridgeQuote?.originTokenForQuote, + previousBridgeQuote?.destChainId, + previousBridgeQuote?.destTokenForQuote + ), + [previousBridgeQuote] ) const hasSameSelectionsAsPreviousQuote = useMemo( @@ -40,16 +46,29 @@ export const useConfirmNewBridgePrice = () => { [currentBridgeQuoteSelections, previousBridgeQuoteSelections] ) - const calculateOutputRelativeDifference = useCallback((quoteA, quoteB) => { - if (!quoteA?.outputAmountString || !quoteB?.outputAmountString) return null + useEffect(() => { + const validQuotes = + bridgeQuote?.outputAmount && previousBridgeQuote?.outputAmount - const outputA = parseFloat(quoteA.outputAmountString) - const outputB = parseFloat(quoteB.outputAmountString) + const outputAmountDiffMoreThan1bps = validQuotes + ? calculateOutputRelativeDifference( + bridgeQuote, + quoteRef.current ?? previousBridgeQuote + ) > 0.0001 + : false - return Math.abs(outputA - outputB) / outputB - }, []) + if ( + validQuotes && + outputAmountDiffMoreThan1bps && + hasSameSelectionsAsPreviousQuote + ) { + requestUserConfirmChange(previousBridgeQuote) + } else { + resetConfirm() + } + }, [bridgeQuote, previousBridgeQuote, hasSameSelectionsAsPreviousQuote]) - const handleRequestUserConfirmChange = (previousQuote) => { + const requestUserConfirmChange = (previousQuote: BridgeQuote) => { if (!hasQuoteOutputChanged && !hasUserConfirmedChange) { quoteRef.current = previousQuote setHasQuoteOutputChanged(true) @@ -57,12 +76,7 @@ export const useConfirmNewBridgePrice = () => { setHasUserConfirmedChange(false) } - const handleUserAcceptChange = () => { - quoteRef.current = null - setHasUserConfirmedChange(true) - } - - const handleReset = () => { + const resetConfirm = () => { if (hasUserConfirmedChange) { quoteRef.current = null setHasQuoteOutputChanged(false) @@ -70,54 +84,27 @@ export const useConfirmNewBridgePrice = () => { } } - useEffect(() => { - const validQuotes = - bridgeQuote?.outputAmount && previousBridgeQuote?.outputAmount - const selectionsMatch = - currentBridgeQuoteSelections === previousBridgeQuoteSelections - - const outputAmountDiffMoreThan1bps = validQuotes - ? calculateOutputRelativeDifference( - bridgeQuote, - quoteRef.current ?? previousBridgeQuote - ) > 0.0001 - : false - - console.log('quoteRef.current:', quoteRef.current?.outputAmountString) - console.log( - 'bridgeQuote?.outputAmountString: ', - bridgeQuote?.outputAmountString - ) - console.log( - 'previousBridgeQuote?.outputAmountString:', - previousBridgeQuote?.outputAmountString - ) - console.log( - 'relative difference: ', - calculateOutputRelativeDifference( - bridgeQuote, - quoteRef.current ?? previousBridgeQuote - ) - ) - console.log('outputAmountDiffMoreThan1bps: ', outputAmountDiffMoreThan1bps) - - if (validQuotes && selectionsMatch && outputAmountDiffMoreThan1bps) { - handleRequestUserConfirmChange(previousBridgeQuote) - } else { - handleReset() - } - }, [ - bridgeQuote, - previousBridgeQuote, - currentBridgeQuoteSelections, - previousBridgeQuoteSelections, - calculateOutputRelativeDifference, - ]) + const onUserAcceptChange = () => { + quoteRef.current = null + setHasUserConfirmedChange(true) + } return { hasSameSelectionsAsPreviousQuote, hasQuoteOutputChanged, hasUserConfirmedChange, - handleUserAcceptChange, + 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 +} From c7b29815629ed43cf8ffef847390cf4ce2561c4a Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:20:29 -0700 Subject: [PATCH 12/31] [WIP] stale quote animation (#3105) * animate-quote-refresh * pair stale quote refresh with each quote * update progress circle animation --- .../StateManagedBridge/OutputContainer.tsx | 49 ++++++++++++++++++- .../pages/state-managed-bridge/index.tsx | 1 - .../utils/hooks/useStaleQuoteUpdater.ts | 25 +++++----- 3 files changed, 59 insertions(+), 16 deletions(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx b/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx index 9f06ed4bd2..03b73bb8f0 100644 --- a/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx @@ -1,5 +1,5 @@ import { useAccount } from 'wagmi' -import { useMemo } from 'react' +import { useMemo, useEffect, useState } from 'react' import { ChainSelector } from '@/components/ui/ChainSelector' import { TokenSelector } from '@/components/ui/TokenSelector' @@ -14,11 +14,13 @@ import { setToChainId, setToToken } from '@/slices/bridge/reducer' import { useBridgeDisplayState, useBridgeState } from '@/slices/bridge/hooks' import { useWalletState } from '@/slices/wallet/hooks' import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks' +import { BridgeQuote } from '@/utils/types' import { useBridgeValidations } from './hooks/useBridgeValidations' +import { useConfirmNewBridgePrice } from './hooks/useConfirmNewBridgePrice' export const OutputContainer = () => { const { address } = useAccount() - const { bridgeQuote, isLoading } = useBridgeQuoteState() + const { bridgeQuote, previousBridgeQuote, isLoading } = useBridgeQuoteState() const { showDestinationAddress } = useBridgeDisplayState() const { hasValidInput, hasValidQuote } = useBridgeValidations() @@ -48,6 +50,9 @@ export const OutputContainer = () => { showValue={showValue} isLoading={isLoading} /> + {hasValidQuote && !isLoading && ( + + )} ) @@ -88,3 +93,43 @@ const ToTokenSelector = () => { /> ) } + +const AnimatedProgressCircle = ({ bridgeQuoteId }) => { + const [animationKey, setAnimationKey] = useState(0) + + useEffect(() => { + setAnimationKey((prevKey) => prevKey + 1) + }, [bridgeQuoteId]) + + return ( + + + + + + + + + ) +} diff --git a/packages/synapse-interface/pages/state-managed-bridge/index.tsx b/packages/synapse-interface/pages/state-managed-bridge/index.tsx index 58e61370c2..43b5e65aa2 100644 --- a/packages/synapse-interface/pages/state-managed-bridge/index.tsx +++ b/packages/synapse-interface/pages/state-managed-bridge/index.tsx @@ -200,7 +200,6 @@ const StateManagedBridge = () => { bridgeQuote, getAndSetBridgeQuote, isLoading, - isWalletPending, quoteTimeout ) diff --git a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts index 05ccd37dc3..f998c777c2 100644 --- a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts +++ b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts @@ -2,7 +2,6 @@ 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' @@ -14,25 +13,19 @@ export const useStaleQuoteUpdater = ( quote: BridgeQuote, refreshQuoteCallback: () => Promise, isQuoteLoading: boolean, - isWalletPending: boolean, staleTimeout: number = 15000 // Default 15_000ms or 15s ) => { const eventListenerRef = useRef void)>(null) - + const timeoutRef = useRef(null) const quoteTime = quote?.id ? convertUuidToUnix(quote?.id) : null const isValidQuote = isNumber(quoteTime) && !isNull(quoteTime) - const currentTime = useIntervalTimer(staleTimeout, !isValidQuote) + 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) - } + if (isValidQuote && !isQuoteLoading) { + timeoutRef.current = setTimeout(() => { + eventListenerRef.current = null const newEventListener = () => { refreshQuoteCallback() @@ -44,7 +37,13 @@ export const useStaleQuoteUpdater = ( }) eventListenerRef.current = newEventListener + }, staleTimeout) + } + + return () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) } } - }, [currentTime, staleTimeout]) + }, [quote, isQuoteLoading]) } From 93dc9da07f1e46875eb485fec834810b6681dbd6 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Mon, 9 Sep 2024 13:05:09 -0700 Subject: [PATCH 13/31] prevent refetch during pending wallet --- .../pages/state-managed-bridge/index.tsx | 6 ++++++ .../utils/hooks/useStaleQuoteUpdater.ts | 10 ++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/synapse-interface/pages/state-managed-bridge/index.tsx b/packages/synapse-interface/pages/state-managed-bridge/index.tsx index 43b5e65aa2..b7c28beba0 100644 --- a/packages/synapse-interface/pages/state-managed-bridge/index.tsx +++ b/packages/synapse-interface/pages/state-managed-bridge/index.tsx @@ -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() @@ -200,6 +201,7 @@ const StateManagedBridge = () => { bridgeQuote, getAndSetBridgeQuote, isLoading, + isWalletPending, quoteTimeout ) @@ -395,6 +397,10 @@ const StateManagedBridge = () => { ) } + if (isTransactionUserRejectedError) { + getAndSetBridgeQuote() + } + return txErrorHandler(error) } finally { dispatch(setIsWalletPending(false)) diff --git a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts index f998c777c2..5054e17476 100644 --- a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts +++ b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts @@ -4,6 +4,7 @@ import { useEffect, useRef } from 'react' import { BridgeQuote } from '@/utils/types' import { useIntervalTimer } from '@/utils/hooks/useIntervalTimer' import { convertUuidToUnix } from '@/utils/convertUuidToUnix' +import { calculateTimeBetween } from '../time' /** * Refreshes quotes based on selected stale timeout duration. @@ -13,17 +14,18 @@ export const useStaleQuoteUpdater = ( quote: BridgeQuote, refreshQuoteCallback: () => Promise, isQuoteLoading: boolean, + isWalletPending: boolean, staleTimeout: number = 15000 // Default 15_000ms or 15s ) => { const eventListenerRef = useRef void)>(null) const timeoutRef = useRef(null) const quoteTime = quote?.id ? convertUuidToUnix(quote?.id) : null - const isValidQuote = isNumber(quoteTime) && !isNull(quoteTime) + const isQuoteValid = isNumber(quoteTime) && !isNull(quoteTime) - useIntervalTimer(staleTimeout, !isValidQuote) + useIntervalTimer(staleTimeout, !isQuoteValid) useEffect(() => { - if (isValidQuote && !isQuoteLoading) { + if (isQuoteValid && !isQuoteLoading && !isWalletPending) { timeoutRef.current = setTimeout(() => { eventListenerRef.current = null @@ -45,5 +47,5 @@ export const useStaleQuoteUpdater = ( clearTimeout(timeoutRef.current) } } - }, [quote, isQuoteLoading]) + }, [quote, isQuoteLoading, isWalletPending]) } From e7e2f318a5b4ad423cbf457b170e4d71c650a046 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Mon, 9 Sep 2024 13:39:15 -0700 Subject: [PATCH 14/31] update reset animation --- .../AnimatedProgressCircle.tsx | 40 +++++++++++++++ .../BridgeTransactionButton.tsx | 12 +++-- .../StateManagedBridge/OutputContainer.tsx | 49 +------------------ .../components/buttons/TransactionButton.tsx | 6 ++- .../utils/hooks/useStaleQuoteUpdater.ts | 1 - 5 files changed, 55 insertions(+), 53 deletions(-) create mode 100644 packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx diff --git a/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx b/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx new file mode 100644 index 0000000000..2c06d6de65 --- /dev/null +++ b/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx @@ -0,0 +1,40 @@ +import { BridgeQuote } from '@/utils/types' +import { useState, useEffect } from 'react' + +export const BridgeQuoteResetTimer = ({ + bridgeQuote, + hasValidQuote, +}: { + bridgeQuote: BridgeQuote + hasValidQuote: boolean +}) => { + if (hasValidQuote) { + return + } +} + +const AnimatedProgressCircle = ({ animateKey }) => { + const [animationKey, setAnimationKey] = useState(0) + + useEffect(() => { + setAnimationKey((prevKey) => prevKey + 1) + }, [animateKey]) + + return ( + + + + + + + ) +} diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx index 975d139e27..bfaa066db5 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx @@ -9,12 +9,10 @@ import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks' import { setIsDestinationWarningAccepted } from '@/slices/bridgeDisplaySlice' import { useBridgeDisplayState, useBridgeState } from '@/slices/bridge/hooks' import { TransactionButton } from '@/components/buttons/TransactionButton' -import { - useBridgeValidations, - constructStringifiedBridgeSelections, -} from './hooks/useBridgeValidations' +import { useBridgeValidations } from './hooks/useBridgeValidations' import { segmentAnalyticsEvent } from '@/contexts/SegmentAnalyticsProvider' import { useConfirmNewBridgePrice } from './hooks/useConfirmNewBridgePrice' +import { BridgeQuoteResetTimer } from './AnimatedProgressCircle' export const BridgeTransactionButton = ({ approveTxn, @@ -195,6 +193,12 @@ export const BridgeTransactionButton = ({ onClick: executeBridge, label: `Bridge ${fromToken?.symbol}`, pendingLabel: 'Bridging', + labelAnimation: ( + + ), } } diff --git a/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx b/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx index 03b73bb8f0..9f06ed4bd2 100644 --- a/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx @@ -1,5 +1,5 @@ import { useAccount } from 'wagmi' -import { useMemo, useEffect, useState } from 'react' +import { useMemo } from 'react' import { ChainSelector } from '@/components/ui/ChainSelector' import { TokenSelector } from '@/components/ui/TokenSelector' @@ -14,13 +14,11 @@ import { setToChainId, setToToken } from '@/slices/bridge/reducer' import { useBridgeDisplayState, useBridgeState } from '@/slices/bridge/hooks' import { useWalletState } from '@/slices/wallet/hooks' import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks' -import { BridgeQuote } from '@/utils/types' import { useBridgeValidations } from './hooks/useBridgeValidations' -import { useConfirmNewBridgePrice } from './hooks/useConfirmNewBridgePrice' export const OutputContainer = () => { const { address } = useAccount() - const { bridgeQuote, previousBridgeQuote, isLoading } = useBridgeQuoteState() + const { bridgeQuote, isLoading } = useBridgeQuoteState() const { showDestinationAddress } = useBridgeDisplayState() const { hasValidInput, hasValidQuote } = useBridgeValidations() @@ -50,9 +48,6 @@ export const OutputContainer = () => { showValue={showValue} isLoading={isLoading} /> - {hasValidQuote && !isLoading && ( - - )} ) @@ -93,43 +88,3 @@ const ToTokenSelector = () => { /> ) } - -const AnimatedProgressCircle = ({ bridgeQuoteId }) => { - const [animationKey, setAnimationKey] = useState(0) - - useEffect(() => { - setAnimationKey((prevKey) => prevKey + 1) - }, [bridgeQuoteId]) - - return ( - - - - - - - - - ) -} diff --git a/packages/synapse-interface/components/buttons/TransactionButton.tsx b/packages/synapse-interface/components/buttons/TransactionButton.tsx index 8decf4bb50..4cfe69c81a 100644 --- a/packages/synapse-interface/components/buttons/TransactionButton.tsx +++ b/packages/synapse-interface/components/buttons/TransactionButton.tsx @@ -19,6 +19,7 @@ export const TransactionButton = ({ onClick, pendingLabel, label, + labelAnimation, onSuccess, disabled, chainId, @@ -29,6 +30,7 @@ export const TransactionButton = ({ onClick: () => Promise pendingLabel: string label: string + labelAnimation?: React.ReactNode onSuccess?: () => void chainId?: number style?: CSSProperties @@ -63,7 +65,9 @@ export const TransactionButton = ({ {pendingLabel}{' '} ) : ( - <>{label} + <> + {label} {labelAnimation} + )} ) diff --git a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts index 5054e17476..aec990f212 100644 --- a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts +++ b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts @@ -4,7 +4,6 @@ import { useEffect, useRef } from 'react' import { BridgeQuote } from '@/utils/types' import { useIntervalTimer } from '@/utils/hooks/useIntervalTimer' import { convertUuidToUnix } from '@/utils/convertUuidToUnix' -import { calculateTimeBetween } from '../time' /** * Refreshes quotes based on selected stale timeout duration. From 32de57cefd56b1d0976cefefb949308f62415015 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Mon, 9 Sep 2024 14:00:47 -0700 Subject: [PATCH 15/31] disable bridge button when quote stale --- .../AnimatedProgressCircle.tsx | 24 ++++++++++++++++--- .../BridgeTransactionButton.tsx | 4 ++++ .../pages/state-managed-bridge/index.tsx | 6 ++++- .../utils/hooks/useStaleQuoteUpdater.ts | 9 ++++++- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx b/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx index 2c06d6de65..c7e9c6480c 100644 --- a/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx @@ -4,16 +4,26 @@ import { useState, useEffect } from 'react' export const BridgeQuoteResetTimer = ({ bridgeQuote, hasValidQuote, + duration, // in ms }: { bridgeQuote: BridgeQuote hasValidQuote: boolean + duration: number }) => { if (hasValidQuote) { - return + return ( + + ) } } -const AnimatedProgressCircle = ({ animateKey }) => { +const AnimatedProgressCircle = ({ + animateKey, + duration, +}: { + animateKey: string + duration: number +}) => { const [animationKey, setAnimationKey] = useState(0) useEffect(() => { @@ -33,8 +43,16 @@ const AnimatedProgressCircle = ({ animateKey }) => { > - + ) } + +const convertMsToSeconds = (ms: number) => { + return Math.ceil(ms / 1000) +} diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx index bfaa066db5..d9eb7191bd 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx @@ -20,6 +20,8 @@ export const BridgeTransactionButton = ({ isApproved, isBridgePaused, isTyping, + isQuoteStale, + quoteTimeout, }) => { const dispatch = useAppDispatch() const { openConnectModal } = useConnectModal() @@ -70,6 +72,7 @@ export const BridgeTransactionButton = ({ const isButtonDisabled = isBridgePaused || + isQuoteStale || isTyping || isLoading || isWalletPending || @@ -197,6 +200,7 @@ export const BridgeTransactionButton = ({ ), } diff --git a/packages/synapse-interface/pages/state-managed-bridge/index.tsx b/packages/synapse-interface/pages/state-managed-bridge/index.tsx index b7c28beba0..efc6b4032a 100644 --- a/packages/synapse-interface/pages/state-managed-bridge/index.tsx +++ b/packages/synapse-interface/pages/state-managed-bridge/index.tsx @@ -197,7 +197,7 @@ const StateManagedBridge = () => { } } - useStaleQuoteUpdater( + const isStale = useStaleQuoteUpdater( bridgeQuote, getAndSetBridgeQuote, isLoading, @@ -205,6 +205,8 @@ const StateManagedBridge = () => { quoteTimeout ) + console.log('isStale: ', isStale) + const approveTxn = async () => { try { dispatch(setIsWalletPending(true)) @@ -455,6 +457,8 @@ const StateManagedBridge = () => { approveTxn={approveTxn} executeBridge={executeBridge} isBridgePaused={isBridgePaused} + isQuoteStale={isStale} + quoteTimeout={quoteTimeout} /> )} diff --git a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts index aec990f212..0482f827c0 100644 --- a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts +++ b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts @@ -1,5 +1,5 @@ import { isNull, isNumber } from 'lodash' -import { useEffect, useRef } from 'react' +import { useEffect, useRef, useState } from 'react' import { BridgeQuote } from '@/utils/types' import { useIntervalTimer } from '@/utils/hooks/useIntervalTimer' @@ -16,8 +16,10 @@ export const useStaleQuoteUpdater = ( isWalletPending: boolean, staleTimeout: number = 15000 // Default 15_000ms or 15s ) => { + const [isStale, setIsStale] = useState(false) const eventListenerRef = useRef void)>(null) const timeoutRef = useRef(null) + const quoteTime = quote?.id ? convertUuidToUnix(quote?.id) : null const isQuoteValid = isNumber(quoteTime) && !isNull(quoteTime) @@ -27,10 +29,12 @@ export const useStaleQuoteUpdater = ( if (isQuoteValid && !isQuoteLoading && !isWalletPending) { timeoutRef.current = setTimeout(() => { eventListenerRef.current = null + setIsStale(true) const newEventListener = () => { refreshQuoteCallback() eventListenerRef.current = null + setIsStale(false) } document.addEventListener('mousemove', newEventListener, { @@ -44,7 +48,10 @@ export const useStaleQuoteUpdater = ( return () => { if (timeoutRef.current) { clearTimeout(timeoutRef.current) + setIsStale(false) } } }, [quote, isQuoteLoading, isWalletPending]) + + return isStale } From f1118b16046698fba9621910f9ceedaafd84a45f Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Mon, 9 Sep 2024 14:08:39 -0700 Subject: [PATCH 16/31] yarn install --- .../pages/state-managed-bridge/index.tsx | 2 - yarn.lock | 689 +----------------- 2 files changed, 12 insertions(+), 679 deletions(-) diff --git a/packages/synapse-interface/pages/state-managed-bridge/index.tsx b/packages/synapse-interface/pages/state-managed-bridge/index.tsx index efc6b4032a..9682661272 100644 --- a/packages/synapse-interface/pages/state-managed-bridge/index.tsx +++ b/packages/synapse-interface/pages/state-managed-bridge/index.tsx @@ -205,8 +205,6 @@ const StateManagedBridge = () => { quoteTimeout ) - console.log('isStale: ', isStale) - const approveTxn = async () => { try { dispatch(setIsWalletPending(true)) diff --git a/yarn.lock b/yarn.lock index f4f9efcc96..e2524fe19d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -422,16 +422,6 @@ "@jridgewell/trace-mapping" "^0.3.25" jsesc "^2.5.1" -"@babel/generator@^7.25.6": - version "7.25.6" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.25.6.tgz#0df1ad8cb32fe4d2b01d8bf437f153d19342a87c" - integrity sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw== - dependencies: - "@babel/types" "^7.25.6" - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.25" - jsesc "^2.5.1" - "@babel/helper-annotate-as-pure@^7.18.6", "@babel/helper-annotate-as-pure@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" @@ -850,13 +840,6 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.8.tgz#58a4dbbcad7eb1d48930524a3fd93d93e9084c6f" integrity sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w== -"@babel/parser@^7.25.0", "@babel/parser@^7.25.6": - version "7.25.6" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.25.6.tgz#85660c5ef388cbbf6e3d2a694ee97a38f18afe2f" - integrity sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q== - dependencies: - "@babel/types" "^7.25.6" - "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.24.5": version "7.24.5" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.5.tgz#4c3685eb9cd790bcad2843900fe0250c91ccf895" @@ -2015,15 +1998,6 @@ "@babel/parser" "^7.24.7" "@babel/types" "^7.24.7" -"@babel/template@^7.25.0": - version "7.25.0" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.0.tgz#e733dc3134b4fede528c15bc95e89cb98c52592a" - integrity sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q== - dependencies: - "@babel/code-frame" "^7.24.7" - "@babel/parser" "^7.25.0" - "@babel/types" "^7.25.0" - "@babel/traverse@^7.1.0", "@babel/traverse@^7.1.6", "@babel/traverse@^7.11.5", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.14.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.23.2", "@babel/traverse@^7.24.1", "@babel/traverse@^7.7.0": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.1.tgz#d65c36ac9dd17282175d1e4a3c49d5b7988f530c" @@ -2072,19 +2046,6 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/traverse@^7.7.2": - version "7.25.6" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.6.tgz#04fad980e444f182ecf1520504941940a90fea41" - integrity sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ== - dependencies: - "@babel/code-frame" "^7.24.7" - "@babel/generator" "^7.25.6" - "@babel/parser" "^7.25.6" - "@babel/template" "^7.25.0" - "@babel/types" "^7.25.6" - debug "^4.3.1" - globals "^11.1.0" - "@babel/types@^7.0.0", "@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.2.0", "@babel/types@^7.20.7", "@babel/types@^7.22.10", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.24.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": version "7.24.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.0.tgz#3b951f435a92e7333eba05b7566fd297960ea1bf" @@ -2112,15 +2073,6 @@ "@babel/helper-validator-identifier" "^7.24.7" to-fast-properties "^2.0.0" -"@babel/types@^7.25.0", "@babel/types@^7.25.6": - version "7.25.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.6.tgz#893942ddb858f32ae7a004ec9d3a76b3463ef8e6" - integrity sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw== - dependencies: - "@babel/helper-string-parser" "^7.24.8" - "@babel/helper-validator-identifier" "^7.24.7" - to-fast-properties "^2.0.0" - "@base2/pretty-print-object@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@base2/pretty-print-object/-/pretty-print-object-1.0.1.tgz#371ba8be66d556812dc7fb169ebc3c08378f69d4" @@ -4577,18 +4529,6 @@ jest-util "^25.5.0" slash "^3.0.0" -"@jest/console@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-28.1.3.tgz#2030606ec03a18c31803b8a36382762e447655df" - integrity sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw== - dependencies: - "@jest/types" "^28.1.3" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - slash "^3.0.0" - "@jest/console@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" @@ -4635,41 +4575,6 @@ slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/core@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-28.1.3.tgz#0ebf2bd39840f1233cd5f2d1e6fc8b71bd5a1ac7" - integrity sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA== - dependencies: - "@jest/console" "^28.1.3" - "@jest/reporters" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - ci-info "^3.2.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-changed-files "^28.1.3" - jest-config "^28.1.3" - jest-haste-map "^28.1.3" - jest-message-util "^28.1.3" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-resolve-dependencies "^28.1.3" - jest-runner "^28.1.3" - jest-runtime "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" - jest-watcher "^28.1.3" - micromatch "^4.0.4" - pretty-format "^28.1.3" - rimraf "^3.0.0" - slash "^3.0.0" - strip-ansi "^6.0.0" - "@jest/core@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" @@ -4713,16 +4618,6 @@ "@jest/types" "^25.5.0" jest-mock "^25.5.0" -"@jest/environment@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-28.1.3.tgz#abed43a6b040a4c24fdcb69eab1f97589b2d663e" - integrity sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA== - dependencies: - "@jest/fake-timers" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - jest-mock "^28.1.3" - "@jest/environment@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" @@ -4733,13 +4628,6 @@ "@types/node" "*" jest-mock "^29.7.0" -"@jest/expect-utils@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525" - integrity sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA== - dependencies: - jest-get-type "^28.0.2" - "@jest/expect-utils@^29.6.2", "@jest/expect-utils@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" @@ -4747,14 +4635,6 @@ dependencies: jest-get-type "^29.6.3" -"@jest/expect@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-28.1.3.tgz#9ac57e1d4491baca550f6bdbd232487177ad6a72" - integrity sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw== - dependencies: - expect "^28.1.3" - jest-snapshot "^28.1.3" - "@jest/expect@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" @@ -4774,18 +4654,6 @@ jest-util "^25.5.0" lolex "^5.0.0" -"@jest/fake-timers@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-28.1.3.tgz#230255b3ad0a3d4978f1d06f70685baea91c640e" - integrity sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw== - dependencies: - "@jest/types" "^28.1.3" - "@sinonjs/fake-timers" "^9.1.2" - "@types/node" "*" - jest-message-util "^28.1.3" - jest-mock "^28.1.3" - jest-util "^28.1.3" - "@jest/fake-timers@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" @@ -4807,15 +4675,6 @@ "@jest/types" "^25.5.0" expect "^25.5.0" -"@jest/globals@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-28.1.3.tgz#a601d78ddc5fdef542728309894895b4a42dc333" - integrity sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA== - dependencies: - "@jest/environment" "^28.1.3" - "@jest/expect" "^28.1.3" - "@jest/types" "^28.1.3" - "@jest/globals@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" @@ -4858,37 +4717,6 @@ optionalDependencies: node-notifier "^6.0.0" -"@jest/reporters@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-28.1.3.tgz#9adf6d265edafc5fc4a434cfb31e2df5a67a369a" - integrity sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@jridgewell/trace-mapping" "^0.3.13" - "@types/node" "*" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^5.1.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - jest-worker "^28.1.3" - slash "^3.0.0" - string-length "^4.0.1" - strip-ansi "^6.0.0" - terminal-link "^2.0.0" - v8-to-istanbul "^9.0.1" - "@jest/reporters@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" @@ -4919,13 +4747,6 @@ strip-ansi "^6.0.0" v8-to-istanbul "^9.0.1" -"@jest/schemas@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" - integrity sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg== - dependencies: - "@sinclair/typebox" "^0.24.1" - "@jest/schemas@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" @@ -4942,15 +4763,6 @@ graceful-fs "^4.2.4" source-map "^0.6.0" -"@jest/source-map@^28.1.2": - version "28.1.2" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-28.1.2.tgz#7fe832b172b497d6663cdff6c13b0a920e139e24" - integrity sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww== - dependencies: - "@jridgewell/trace-mapping" "^0.3.13" - callsites "^3.0.0" - graceful-fs "^4.2.9" - "@jest/source-map@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" @@ -4970,16 +4782,6 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-result@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-28.1.3.tgz#5eae945fd9f4b8fcfce74d239e6f725b6bf076c5" - integrity sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg== - dependencies: - "@jest/console" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - "@jest/test-result@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" @@ -5001,16 +4803,6 @@ jest-runner "^25.5.4" jest-runtime "^25.5.4" -"@jest/test-sequencer@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz#9d0c283d906ac599c74bde464bc0d7e6a82886c3" - integrity sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw== - dependencies: - "@jest/test-result" "^28.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - slash "^3.0.0" - "@jest/test-sequencer@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" @@ -5064,27 +4856,6 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/transform@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-28.1.3.tgz#59d8098e50ab07950e0f2fc0fc7ec462371281b0" - integrity sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA== - dependencies: - "@babel/core" "^7.11.6" - "@jest/types" "^28.1.3" - "@jridgewell/trace-mapping" "^0.3.13" - babel-plugin-istanbul "^6.1.1" - chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - jest-regex-util "^28.0.2" - jest-util "^28.1.3" - micromatch "^4.0.4" - pirates "^4.0.4" - slash "^3.0.0" - write-file-atomic "^4.0.1" - "@jest/transform@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" @@ -5147,18 +4918,6 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jest/types@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b" - integrity sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ== - dependencies: - "@jest/schemas" "^28.1.3" - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^17.0.8" - chalk "^4.0.0" - "@jest/types@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" @@ -5211,7 +4970,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== @@ -7556,11 +7315,6 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== -"@sinclair/typebox@^0.24.1": - version "0.24.51" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" - integrity sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA== - "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -7607,13 +7361,6 @@ dependencies: "@sinonjs/commons" "^3.0.0" -"@sinonjs/fake-timers@^9.1.2": - version "9.1.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" - integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== - dependencies: - "@sinonjs/commons" "^1.7.0" - "@slorber/remark-comment@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@slorber/remark-comment/-/remark-comment-1.0.0.tgz#2a020b3f4579c89dec0361673206c28d67e08f5a" @@ -9790,6 +9537,14 @@ dependencies: tslib "^2.4.0" +"@synapsecns/coverage-aggregator@file:./packages/coverage-aggregator": + version "1.0.6" + dependencies: + glob "^8.0.3" + path "^0.12.7" + ts-jest "^29.0.5" + yargs "^17.6.2" + "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -10684,7 +10439,7 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ== -"@types/prettier@^2.1.1", "@types/prettier@^2.1.5": +"@types/prettier@^2.1.1": version "2.7.3" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== @@ -13258,19 +13013,6 @@ babel-jest@^25.2.6, babel-jest@^25.5.1: graceful-fs "^4.2.4" slash "^3.0.0" -babel-jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-28.1.3.tgz#c1187258197c099072156a0a121c11ee1e3917d5" - integrity sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q== - dependencies: - "@jest/transform" "^28.1.3" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^28.1.3" - chalk "^4.0.0" - graceful-fs "^4.2.9" - slash "^3.0.0" - babel-jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" @@ -13375,16 +13117,6 @@ babel-plugin-jest-hoist@^25.5.0: "@babel/types" "^7.3.3" "@types/babel__traverse" "^7.0.6" -babel-plugin-jest-hoist@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz#1952c4d0ea50f2d6d794353762278d1d8cca3fbe" - integrity sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q== - dependencies: - "@babel/template" "^7.3.3" - "@babel/types" "^7.3.3" - "@types/babel__core" "^7.1.14" - "@types/babel__traverse" "^7.0.6" - babel-plugin-jest-hoist@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" @@ -13577,14 +13309,6 @@ babel-preset-jest@^25.5.0: babel-plugin-jest-hoist "^25.5.0" babel-preset-current-node-syntax "^0.1.2" -babel-preset-jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz#5dfc20b99abed5db994406c2b9ab94c73aaa419d" - integrity sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A== - dependencies: - babel-plugin-jest-hoist "^28.1.3" - babel-preset-current-node-syntax "^1.0.0" - babel-preset-jest@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" @@ -17046,11 +16770,6 @@ diff-sequences@^25.2.6: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd" integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg== -diff-sequences@^28.1.1: - version "28.1.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6" - integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw== - diff-sequences@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" @@ -17491,11 +17210,6 @@ elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4, elliptic@^6. minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -emittery@^0.10.2: - version "0.10.2" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" - integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw== - emittery@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" @@ -19276,17 +18990,6 @@ expect@^25.5.0: jest-message-util "^25.5.0" jest-regex-util "^25.2.6" -expect@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/expect/-/expect-28.1.3.tgz#90a7c1a124f1824133dd4533cce2d2bdcb6603ec" - integrity sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g== - dependencies: - "@jest/expect-utils" "^28.1.3" - jest-get-type "^28.0.2" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - expect@^29.0.0: version "29.6.2" resolved "https://registry.yarnpkg.com/expect/-/expect-29.6.2.tgz#7b08e83eba18ddc4a2cf62b5f2d1918f5cd84521" @@ -23195,7 +22898,7 @@ istanbul-lib-instrument@^4.0.0: istanbul-lib-coverage "^3.0.0" semver "^6.3.0" -istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: +istanbul-lib-instrument@^5.0.4: version "5.2.1" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== @@ -23313,14 +23016,6 @@ jest-changed-files@^25.5.0: execa "^3.2.0" throat "^5.0.0" -jest-changed-files@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-28.1.3.tgz#d9aeee6792be3686c47cb988a8eaf82ff4238831" - integrity sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA== - dependencies: - execa "^5.0.0" - p-limit "^3.1.0" - jest-changed-files@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" @@ -23330,31 +23025,6 @@ jest-changed-files@^29.7.0: jest-util "^29.7.0" p-limit "^3.1.0" -jest-circus@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-28.1.3.tgz#d14bd11cf8ee1a03d69902dc47b6bd4634ee00e4" - integrity sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow== - dependencies: - "@jest/environment" "^28.1.3" - "@jest/expect" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^0.7.0" - is-generator-fn "^2.0.0" - jest-each "^28.1.3" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-runtime "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" - p-limit "^3.1.0" - pretty-format "^28.1.3" - slash "^3.0.0" - stack-utils "^2.0.3" - jest-circus@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" @@ -23401,24 +23071,6 @@ jest-cli@^25.5.4: realpath-native "^2.0.0" yargs "^15.3.1" -jest-cli@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-28.1.3.tgz#558b33c577d06de55087b8448d373b9f654e46b2" - integrity sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ== - dependencies: - "@jest/core" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - import-local "^3.0.2" - jest-config "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" - prompts "^2.0.1" - yargs "^17.3.1" - jest-cli@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" @@ -23461,34 +23113,6 @@ jest-config@^25.5.4: pretty-format "^25.5.0" realpath-native "^2.0.0" -jest-config@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-28.1.3.tgz#e315e1f73df3cac31447eed8b8740a477392ec60" - integrity sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ== - dependencies: - "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^28.1.3" - "@jest/types" "^28.1.3" - babel-jest "^28.1.3" - chalk "^4.0.0" - ci-info "^3.2.0" - deepmerge "^4.2.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-circus "^28.1.3" - jest-environment-node "^28.1.3" - jest-get-type "^28.0.2" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-runner "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" - micromatch "^4.0.4" - parse-json "^5.2.0" - pretty-format "^28.1.3" - slash "^3.0.0" - strip-json-comments "^3.1.1" - jest-config@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" @@ -23537,16 +23161,6 @@ jest-diff@^25.2.1, jest-diff@^25.5.0: jest-get-type "^25.2.6" pretty-format "^25.5.0" -jest-diff@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.3.tgz#948a192d86f4e7a64c5264ad4da4877133d8792f" - integrity sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw== - dependencies: - chalk "^4.0.0" - diff-sequences "^28.1.1" - jest-get-type "^28.0.2" - pretty-format "^28.1.3" - jest-diff@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" @@ -23564,13 +23178,6 @@ jest-docblock@^25.3.0: dependencies: detect-newline "^3.0.0" -jest-docblock@^28.1.1: - version "28.1.1" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-28.1.1.tgz#6f515c3bf841516d82ecd57a62eed9204c2f42a8" - integrity sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA== - dependencies: - detect-newline "^3.0.0" - jest-docblock@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" @@ -23589,17 +23196,6 @@ jest-each@^25.5.0: jest-util "^25.5.0" pretty-format "^25.5.0" -jest-each@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-28.1.3.tgz#bdd1516edbe2b1f3569cfdad9acd543040028f81" - integrity sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g== - dependencies: - "@jest/types" "^28.1.3" - chalk "^4.0.0" - jest-get-type "^28.0.2" - jest-util "^28.1.3" - pretty-format "^28.1.3" - jest-each@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" @@ -23635,18 +23231,6 @@ jest-environment-node@^25.5.0: jest-util "^25.5.0" semver "^6.3.0" -jest-environment-node@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-28.1.3.tgz#7e74fe40eb645b9d56c0c4b70ca4357faa349be5" - integrity sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A== - dependencies: - "@jest/environment" "^28.1.3" - "@jest/fake-timers" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - jest-mock "^28.1.3" - jest-util "^28.1.3" - jest-environment-node@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" @@ -23677,11 +23261,6 @@ jest-get-type@^25.2.6: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877" integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig== -jest-get-type@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203" - integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA== - jest-get-type@^29.4.3, jest-get-type@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" @@ -23728,25 +23307,6 @@ jest-haste-map@^26.6.2: optionalDependencies: fsevents "^2.1.2" -jest-haste-map@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-28.1.3.tgz#abd5451129a38d9841049644f34b034308944e2b" - integrity sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA== - dependencies: - "@jest/types" "^28.1.3" - "@types/graceful-fs" "^4.1.3" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.9" - jest-regex-util "^28.0.2" - jest-util "^28.1.3" - jest-worker "^28.1.3" - micromatch "^4.0.4" - walker "^1.0.8" - optionalDependencies: - fsevents "^2.3.2" - jest-haste-map@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" @@ -23797,14 +23357,6 @@ jest-leak-detector@^25.5.0: jest-get-type "^25.2.6" pretty-format "^25.5.0" -jest-leak-detector@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz#a6685d9b074be99e3adee816ce84fd30795e654d" - integrity sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA== - dependencies: - jest-get-type "^28.0.2" - pretty-format "^28.1.3" - jest-leak-detector@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" @@ -23823,16 +23375,6 @@ jest-matcher-utils@^25.5.0: jest-get-type "^25.2.6" pretty-format "^25.5.0" -jest-matcher-utils@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e" - integrity sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw== - dependencies: - chalk "^4.0.0" - jest-diff "^28.1.3" - jest-get-type "^28.0.2" - pretty-format "^28.1.3" - jest-matcher-utils@^29.6.2, jest-matcher-utils@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" @@ -23857,21 +23399,6 @@ jest-message-util@^25.5.0: slash "^3.0.0" stack-utils "^1.0.1" -jest-message-util@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d" - integrity sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^28.1.3" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^28.1.3" - slash "^3.0.0" - stack-utils "^2.0.3" - jest-message-util@^29.6.2, jest-message-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" @@ -23909,14 +23436,6 @@ jest-mock@^27.0.6: "@jest/types" "^27.5.1" "@types/node" "*" -jest-mock@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-28.1.3.tgz#d4e9b1fc838bea595c77ab73672ebf513ab249da" - integrity sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA== - dependencies: - "@jest/types" "^28.1.3" - "@types/node" "*" - jest-mock@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" @@ -23941,11 +23460,6 @@ jest-regex-util@^26.0.0: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== -jest-regex-util@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-28.0.2.tgz#afdc377a3b25fb6e80825adcf76c854e5bf47ead" - integrity sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw== - jest-regex-util@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" @@ -23960,14 +23474,6 @@ jest-resolve-dependencies@^25.5.4: jest-regex-util "^25.2.6" jest-snapshot "^25.5.1" -jest-resolve-dependencies@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz#8c65d7583460df7275c6ea2791901fa975c1fe66" - integrity sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA== - dependencies: - jest-regex-util "^28.0.2" - jest-snapshot "^28.1.3" - jest-resolve-dependencies@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" @@ -23991,21 +23497,6 @@ jest-resolve@^25.5.1: resolve "^1.17.0" slash "^3.0.0" -jest-resolve@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-28.1.3.tgz#cfb36100341ddbb061ec781426b3c31eb51aa0a8" - integrity sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ== - dependencies: - chalk "^4.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - jest-pnp-resolver "^1.2.2" - jest-util "^28.1.3" - jest-validate "^28.1.3" - resolve "^1.20.0" - resolve.exports "^1.1.0" - slash "^3.0.0" - jest-resolve@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" @@ -24046,33 +23537,6 @@ jest-runner@^25.5.4: source-map-support "^0.5.6" throat "^5.0.0" -jest-runner@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-28.1.3.tgz#5eee25febd730b4713a2cdfd76bdd5557840f9a1" - integrity sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA== - dependencies: - "@jest/console" "^28.1.3" - "@jest/environment" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.10.2" - graceful-fs "^4.2.9" - jest-docblock "^28.1.1" - jest-environment-node "^28.1.3" - jest-haste-map "^28.1.3" - jest-leak-detector "^28.1.3" - jest-message-util "^28.1.3" - jest-resolve "^28.1.3" - jest-runtime "^28.1.3" - jest-util "^28.1.3" - jest-watcher "^28.1.3" - jest-worker "^28.1.3" - p-limit "^3.1.0" - source-map-support "0.5.13" - jest-runner@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" @@ -24132,34 +23596,6 @@ jest-runtime@^25.5.4: strip-bom "^4.0.0" yargs "^15.3.1" -jest-runtime@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-28.1.3.tgz#a57643458235aa53e8ec7821949e728960d0605f" - integrity sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw== - dependencies: - "@jest/environment" "^28.1.3" - "@jest/fake-timers" "^28.1.3" - "@jest/globals" "^28.1.3" - "@jest/source-map" "^28.1.2" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - execa "^5.0.0" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - jest-message-util "^28.1.3" - jest-mock "^28.1.3" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" - slash "^3.0.0" - strip-bom "^4.0.0" - jest-runtime@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" @@ -24224,35 +23660,6 @@ jest-snapshot@^25.5.1: pretty-format "^25.5.0" semver "^6.3.0" -jest-snapshot@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-28.1.3.tgz#17467b3ab8ddb81e2f605db05583d69388fc0668" - integrity sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg== - dependencies: - "@babel/core" "^7.11.6" - "@babel/generator" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" - "@babel/types" "^7.3.3" - "@jest/expect-utils" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/babel__traverse" "^7.0.6" - "@types/prettier" "^2.1.5" - babel-preset-current-node-syntax "^1.0.0" - chalk "^4.0.0" - expect "^28.1.3" - graceful-fs "^4.2.9" - jest-diff "^28.1.3" - jest-get-type "^28.0.2" - jest-haste-map "^28.1.3" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - natural-compare "^1.4.0" - pretty-format "^28.1.3" - semver "^7.3.5" - jest-snapshot@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" @@ -24302,18 +23709,6 @@ jest-util@^26.6.2: is-ci "^2.0.0" micromatch "^4.0.2" -jest-util@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0" - integrity sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ== - dependencies: - "@jest/types" "^28.1.3" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - jest-util@^29.0.0, jest-util@^29.6.2, jest-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" @@ -24338,18 +23733,6 @@ jest-validate@^25.5.0: leven "^3.1.0" pretty-format "^25.5.0" -jest-validate@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-28.1.3.tgz#e322267fd5e7c64cea4629612c357bbda96229df" - integrity sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA== - dependencies: - "@jest/types" "^28.1.3" - camelcase "^6.2.0" - chalk "^4.0.0" - jest-get-type "^28.0.2" - leven "^3.1.0" - pretty-format "^28.1.3" - jest-validate@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" @@ -24387,20 +23770,6 @@ jest-watcher@^25.2.4, jest-watcher@^25.5.0: jest-util "^25.5.0" string-length "^3.1.0" -jest-watcher@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-28.1.3.tgz#c6023a59ba2255e3b4c57179fc94164b3e73abd4" - integrity sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g== - dependencies: - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - emittery "^0.10.2" - jest-util "^28.1.3" - string-length "^4.0.1" - jest-watcher@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" @@ -24449,15 +23818,6 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-28.1.3.tgz#7e3c4ce3fa23d1bb6accb169e7f396f98ed4bb98" - integrity sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^8.0.0" - jest-worker@^29.1.2, jest-worker@^29.4.3, jest-worker@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" @@ -24477,16 +23837,6 @@ jest@^25.3.0: import-local "^3.0.2" jest-cli "^25.5.4" -jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest/-/jest-28.1.3.tgz#e9c6a7eecdebe3548ca2b18894a50f45b36dfc6b" - integrity sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA== - dependencies: - "@jest/core" "^28.1.3" - "@jest/types" "^28.1.3" - import-local "^3.0.2" - jest-cli "^28.1.3" - jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" @@ -30473,16 +29823,6 @@ pretty-format@^27.0.2: ansi-styles "^5.0.0" react-is "^17.0.1" -pretty-format@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5" - integrity sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q== - dependencies: - "@jest/schemas" "^28.1.3" - ansi-regex "^5.0.1" - ansi-styles "^5.0.0" - react-is "^18.0.0" - pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" @@ -32267,11 +31607,6 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== -resolve.exports@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" - integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== - resolve.exports@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" @@ -38318,7 +37653,7 @@ write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write-file-atomic@^4.0.1, write-file-atomic@^4.0.2: +write-file-atomic@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== From f682d7337b1c78978f764c517c52375c8f02148c Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Mon, 9 Sep 2024 16:26:31 -0700 Subject: [PATCH 17/31] update confirm button tet --- .../components/StateManagedBridge/BridgeTransactionButton.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx index d9eb7191bd..9f9eb3725e 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx @@ -180,7 +180,7 @@ export const BridgeTransactionButton = ({ } } else if (hasQuoteOutputChanged && !hasUserConfirmedChange) { buttonProperties = { - label: 'Confirm new price', + label: 'Confirm new quote', onClick: () => onUserAcceptChange(), className: '!border !border-synapsePurple !from-bgLight !to-bgLight !animate-pulse', From cd83681fd0a1dd59e420536dfa6a197b34857c9c Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Mon, 9 Sep 2024 16:46:52 -0700 Subject: [PATCH 18/31] display greyed out output when stale quote --- .../components/StateManagedBridge/OutputContainer.tsx | 9 ++++++++- packages/synapse-interface/components/ui/AmountInput.tsx | 3 +++ .../pages/state-managed-bridge/index.tsx | 6 +++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx b/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx index 9f06ed4bd2..8cf759561a 100644 --- a/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx @@ -16,7 +16,11 @@ import { useWalletState } from '@/slices/wallet/hooks' import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks' import { useBridgeValidations } from './hooks/useBridgeValidations' -export const OutputContainer = () => { +interface OutputContainerProps { + isQuoteStale: boolean +} + +export const OutputContainer = ({ isQuoteStale }: OutputContainerProps) => { const { address } = useAccount() const { bridgeQuote, isLoading } = useBridgeQuoteState() const { showDestinationAddress } = useBridgeDisplayState() @@ -32,6 +36,8 @@ export const OutputContainer = () => { } }, [bridgeQuote, hasValidInput, hasValidQuote]) + const inputClassName = isQuoteStale ? 'opacity-50' : undefined + return (
@@ -47,6 +53,7 @@ export const OutputContainer = () => { disabled={true} showValue={showValue} isLoading={isLoading} + className={inputClassName} /> 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) => 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/pages/state-managed-bridge/index.tsx b/packages/synapse-interface/pages/state-managed-bridge/index.tsx index 9682661272..2066faaab8 100644 --- a/packages/synapse-interface/pages/state-managed-bridge/index.tsx +++ b/packages/synapse-interface/pages/state-managed-bridge/index.tsx @@ -197,7 +197,7 @@ const StateManagedBridge = () => { } } - const isStale = useStaleQuoteUpdater( + const isQuoteStale = useStaleQuoteUpdater( bridgeQuote, getAndSetBridgeQuote, isLoading, @@ -444,7 +444,7 @@ const StateManagedBridge = () => { }} disabled={isWalletPending} /> - + @@ -455,7 +455,7 @@ const StateManagedBridge = () => { approveTxn={approveTxn} executeBridge={executeBridge} isBridgePaused={isBridgePaused} - isQuoteStale={isStale} + isQuoteStale={isQuoteStale} quoteTimeout={quoteTimeout} /> From c47a4b62fff5ad632aebeeeef9e0f6fec6a99cd6 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Mon, 9 Sep 2024 22:24:51 -0700 Subject: [PATCH 19/31] store threshold as constant --- .../StateManagedBridge/hooks/useConfirmNewBridgePrice.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts index 2f4a43ab74..ddec669c15 100644 --- a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts @@ -7,6 +7,7 @@ import { BridgeQuote } from '@/utils/types' export const useConfirmNewBridgePrice = () => { const quoteRef = useRef(null) + const bpsThreshold = 0.0001 // 1bps const [hasQuoteOutputChanged, setHasQuoteOutputChanged] = useState(false) @@ -50,16 +51,16 @@ export const useConfirmNewBridgePrice = () => { const validQuotes = bridgeQuote?.outputAmount && previousBridgeQuote?.outputAmount - const outputAmountDiffMoreThan1bps = validQuotes + const outputAmountDiffMoreThanThreshold = validQuotes ? calculateOutputRelativeDifference( bridgeQuote, quoteRef.current ?? previousBridgeQuote - ) > 0.0001 + ) > bpsThreshold : false if ( validQuotes && - outputAmountDiffMoreThan1bps && + outputAmountDiffMoreThanThreshold && hasSameSelectionsAsPreviousQuote ) { requestUserConfirmChange(previousBridgeQuote) From 03549a6e83d06a32caf7244d8f71a9964c65e8e5 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Tue, 10 Sep 2024 15:14:00 -0700 Subject: [PATCH 20/31] auto refresher duration, resets on mouse move --- .../AnimatedProgressCircle.tsx | 20 ++- .../BridgeTransactionButton.tsx | 17 ++- .../utils/hooks/useStaleQuoteUpdater.ts | 140 ++++++++++++++++-- 3 files changed, 148 insertions(+), 29 deletions(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx b/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx index c7e9c6480c..6d4273112a 100644 --- a/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx @@ -1,5 +1,5 @@ import { BridgeQuote } from '@/utils/types' -import { useState, useEffect } from 'react' +import { useState, useEffect, useMemo } from 'react' export const BridgeQuoteResetTimer = ({ bridgeQuote, @@ -10,11 +10,19 @@ export const BridgeQuoteResetTimer = ({ hasValidQuote: boolean duration: number }) => { - if (hasValidQuote) { - return ( - - ) - } + const memoizedTimer = useMemo(() => { + if (hasValidQuote) { + return ( + + ) + } + return null + }, [bridgeQuote, hasValidQuote, duration]) + + return memoizedTimer } const AnimatedProgressCircle = ({ diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx index 9f9eb3725e..806b32dfd1 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx @@ -196,13 +196,6 @@ export const BridgeTransactionButton = ({ onClick: executeBridge, label: `Bridge ${fromToken?.symbol}`, pendingLabel: 'Bridging', - labelAnimation: ( - - ), } } @@ -213,6 +206,16 @@ export const BridgeTransactionButton = ({ {...buttonProperties} disabled={isButtonDisabled} chainId={fromChainId} + labelAnimation={ + isConnected && + hasValidQuote && ( + + ) + } /> ) diff --git a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts index 0482f827c0..f0699e97f0 100644 --- a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts +++ b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts @@ -9,49 +9,157 @@ 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, +// isQuoteLoading: boolean, +// isWalletPending: boolean, +// staleTimeout: number = 15000 // Default 15_000ms or 15s +// ) => { +// const [isStale, setIsStale] = useState(false) +// const eventListenerRef = useRef void)>(null) +// const timeoutRef = useRef(null) + +// const quoteTime = quote?.id ? convertUuidToUnix(quote?.id) : null +// const isQuoteValid = isNumber(quoteTime) && !isNull(quoteTime) + +// useIntervalTimer(staleTimeout, !isQuoteValid) + +// useEffect(() => { +// if (isQuoteValid && !isQuoteLoading && !isWalletPending) { +// timeoutRef.current = setTimeout(() => { +// eventListenerRef.current = null +// setIsStale(true) + +// const newEventListener = () => { +// refreshQuoteCallback() +// eventListenerRef.current = null +// setIsStale(false) +// } + +// document.addEventListener('mousemove', newEventListener, { +// once: true, +// }) + +// eventListenerRef.current = newEventListener +// }, staleTimeout) +// } + +// return () => { +// if (timeoutRef.current) { +// clearTimeout(timeoutRef.current) +// setIsStale(false) +// } +// } +// }, [quote, isQuoteLoading, isWalletPending]) + +// return isStale +// } + export const useStaleQuoteUpdater = ( quote: BridgeQuote, refreshQuoteCallback: () => Promise, isQuoteLoading: boolean, isWalletPending: boolean, - staleTimeout: number = 15000 // Default 15_000ms or 15s + staleTimeout: number = 15000, // in ms + autoRefreshDuration: number = 60000 // in ms ) => { const [isStale, setIsStale] = useState(false) const eventListenerRef = useRef void)>(null) const timeoutRef = useRef(null) + const autoRefreshIntervalRef = useRef(null) + const autoRefreshStartTimeRef = useRef(null) + const autoRefreshEndTimeRef = useRef(null) const quoteTime = quote?.id ? convertUuidToUnix(quote?.id) : null - const isQuoteValid = isNumber(quoteTime) && !isNull(quoteTime) + const isValid = isNumber(quoteTime) && !isNull(quoteTime) + + useIntervalTimer(staleTimeout, !isValid) - useIntervalTimer(staleTimeout, !isQuoteValid) + const [moved, reset] = useTrackMouseMove() useEffect(() => { - if (isQuoteValid && !isQuoteLoading && !isWalletPending) { - timeoutRef.current = setTimeout(() => { - eventListenerRef.current = null - setIsStale(true) + console.log('has mouse moved:', moved) + if (moved && autoRefreshStartTimeRef.current) { + console.log('reset auto refresh timer') + autoRefreshStartTimeRef.current = null + reset() + } + }, [quote]) + + // Start auto-refresh logic for 60 seconds + useEffect(() => { + if (isValid && !isQuoteLoading && !isWalletPending) { + // 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 - const newEventListener = () => { + console.log('elapsedTime: ', elapsedTime) + // If autoRefreshDuration hasn't passed, keep auto-refreshing + if (elapsedTime < autoRefreshDuration) { + if (timeoutRef.current) clearTimeout(timeoutRef.current) + if (autoRefreshIntervalRef.current) + clearInterval(autoRefreshIntervalRef.current) + + autoRefreshIntervalRef.current = setInterval(() => { refreshQuoteCallback() + }, staleTimeout) + } else { + // If more than autoRefreshDuration have passed, stop auto-refreshing and switch to mousemove logic + clearInterval(autoRefreshIntervalRef.current) + + timeoutRef.current = setTimeout(() => { eventListenerRef.current = null - setIsStale(false) - } + setIsStale(true) - document.addEventListener('mousemove', newEventListener, { - once: true, - }) + const newEventListener = () => { + refreshQuoteCallback() + eventListenerRef.current = null + setIsStale(false) + } - eventListenerRef.current = newEventListener - }, staleTimeout) + document.addEventListener('mousemove', newEventListener, { + once: true, + }) + + eventListenerRef.current = newEventListener + }, staleTimeout) + } } return () => { if (timeoutRef.current) { clearTimeout(timeoutRef.current) - setIsStale(false) } + if (autoRefreshIntervalRef.current) { + clearInterval(autoRefreshIntervalRef.current) + } + if (autoRefreshEndTimeRef.current) { + clearTimeout(autoRefreshEndTimeRef.current) + } + setIsStale(false) } }, [quote, isQuoteLoading, isWalletPending]) return isStale } + +export const useTrackMouseMove = (): [boolean, () => void] => { + const [moved, setMoved] = useState(false) + + const onMove = () => setMoved(true) + const reset = () => setMoved(false) + + useEffect(() => { + document.addEventListener('mousemove', onMove) + + return () => { + document.removeEventListener('mousemove', onMove) + } + }, []) + + return [moved, reset] +} From e8eb2a0fafbb92f5ab5e97d6ac7b33f2aa533f0f Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Tue, 10 Sep 2024 16:52:34 -0700 Subject: [PATCH 21/31] persist animation after User confirms new quote --- .../AnimatedProgressCircle.tsx | 2 +- .../BridgeTransactionButton.tsx | 12 ------- .../hooks/useConfirmNewBridgePrice.ts | 2 +- .../pages/state-managed-bridge/index.tsx | 34 +++++++++++++------ .../utils/hooks/useStaleQuoteUpdater.ts | 3 -- 5 files changed, 26 insertions(+), 27 deletions(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx b/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx index 6d4273112a..f154dc6a9d 100644 --- a/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx @@ -47,7 +47,7 @@ const AnimatedProgressCircle = ({ stroke="currentcolor" strokeOpacity=".33" fill="none" - className="absolute -rotate-90 -scale-y-100 right-4" + className="absolute -rotate-90 -scale-y-100" > diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx index 806b32dfd1..dcfbebc870 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx @@ -12,7 +12,6 @@ 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, @@ -21,7 +20,6 @@ export const BridgeTransactionButton = ({ isBridgePaused, isTyping, isQuoteStale, - quoteTimeout, }) => { const dispatch = useAppDispatch() const { openConnectModal } = useConnectModal() @@ -206,16 +204,6 @@ export const BridgeTransactionButton = ({ {...buttonProperties} disabled={isButtonDisabled} chainId={fromChainId} - labelAnimation={ - isConnected && - hasValidQuote && ( - - ) - } /> ) diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts index ddec669c15..98e797ba22 100644 --- a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts @@ -7,7 +7,7 @@ import { BridgeQuote } from '@/utils/types' export const useConfirmNewBridgePrice = () => { const quoteRef = useRef(null) - const bpsThreshold = 0.0001 // 1bps + const bpsThreshold = 0.0000000001 // 1bps const [hasQuoteOutputChanged, setHasQuoteOutputChanged] = useState(false) diff --git a/packages/synapse-interface/pages/state-managed-bridge/index.tsx b/packages/synapse-interface/pages/state-managed-bridge/index.tsx index 2066faaab8..ff9cb25d3a 100644 --- a/packages/synapse-interface/pages/state-managed-bridge/index.tsx +++ b/packages/synapse-interface/pages/state-managed-bridge/index.tsx @@ -68,10 +68,12 @@ 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/AnimatedProgressCircle' +import { useBridgeValidations } from '@/components/StateManagedBridge/hooks/useBridgeValidations' const StateManagedBridge = () => { const dispatch = useAppDispatch() - const { address } = useAccount() + const { address, isConnected } = useAccount() const { synapseSDK } = useSynapseContext() const router = useRouter() const { query, pathname } = router @@ -96,6 +98,8 @@ const StateManagedBridge = () => { const isApproved = useIsBridgeApproved() + const { hasValidQuote } = useBridgeValidations() + const { isWalletPending } = useWalletState() const { showSettingsSlideOver } = useSelector( @@ -449,15 +453,25 @@ const StateManagedBridge = () => { - +
+ + {isConnected && hasValidQuote && ( +
+ +
+ )} +
)} diff --git a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts index f0699e97f0..33f9d01d57 100644 --- a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts +++ b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts @@ -79,9 +79,7 @@ export const useStaleQuoteUpdater = ( const [moved, reset] = useTrackMouseMove() useEffect(() => { - console.log('has mouse moved:', moved) if (moved && autoRefreshStartTimeRef.current) { - console.log('reset auto refresh timer') autoRefreshStartTimeRef.current = null reset() } @@ -97,7 +95,6 @@ export const useStaleQuoteUpdater = ( const elapsedTime = Date.now() - autoRefreshStartTimeRef.current - console.log('elapsedTime: ', elapsedTime) // If autoRefreshDuration hasn't passed, keep auto-refreshing if (elapsedTime < autoRefreshDuration) { if (timeoutRef.current) clearTimeout(timeoutRef.current) From 5ad3190b6392ce5e56587bd491a96f2b03fcb93e Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Tue, 10 Sep 2024 16:57:33 -0700 Subject: [PATCH 22/31] update animation --- .../AnimatedProgressCircle.tsx | 9 ++-- .../utils/hooks/useStaleQuoteUpdater.ts | 53 +------------------ 2 files changed, 7 insertions(+), 55 deletions(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx b/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx index f154dc6a9d..d3e6eca82c 100644 --- a/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx @@ -45,16 +45,17 @@ const AnimatedProgressCircle = ({ height="24" viewBox="-12 -12 24 24" stroke="currentcolor" - strokeOpacity=".33" + stroke-opacity=".33" fill="none" - className="absolute -rotate-90 -scale-y-100" + className="absolute -rotate-90" > - + diff --git a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts index 33f9d01d57..7f7212d2c5 100644 --- a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts +++ b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts @@ -5,57 +5,6 @@ import { BridgeQuote } from '@/utils/types' 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, -// isQuoteLoading: boolean, -// isWalletPending: boolean, -// staleTimeout: number = 15000 // Default 15_000ms or 15s -// ) => { -// const [isStale, setIsStale] = useState(false) -// const eventListenerRef = useRef void)>(null) -// const timeoutRef = useRef(null) - -// const quoteTime = quote?.id ? convertUuidToUnix(quote?.id) : null -// const isQuoteValid = isNumber(quoteTime) && !isNull(quoteTime) - -// useIntervalTimer(staleTimeout, !isQuoteValid) - -// useEffect(() => { -// if (isQuoteValid && !isQuoteLoading && !isWalletPending) { -// timeoutRef.current = setTimeout(() => { -// eventListenerRef.current = null -// setIsStale(true) - -// const newEventListener = () => { -// refreshQuoteCallback() -// eventListenerRef.current = null -// setIsStale(false) -// } - -// document.addEventListener('mousemove', newEventListener, { -// once: true, -// }) - -// eventListenerRef.current = newEventListener -// }, staleTimeout) -// } - -// return () => { -// if (timeoutRef.current) { -// clearTimeout(timeoutRef.current) -// setIsStale(false) -// } -// } -// }, [quote, isQuoteLoading, isWalletPending]) - -// return isStale -// } - export const useStaleQuoteUpdater = ( quote: BridgeQuote, refreshQuoteCallback: () => Promise, @@ -82,6 +31,7 @@ export const useStaleQuoteUpdater = ( if (moved && autoRefreshStartTimeRef.current) { autoRefreshStartTimeRef.current = null reset() + console.log('reset autorefresh') } }, [quote]) @@ -95,6 +45,7 @@ export const useStaleQuoteUpdater = ( const elapsedTime = Date.now() - autoRefreshStartTimeRef.current + console.log('elapsedTime: ', elapsedTime) // If autoRefreshDuration hasn't passed, keep auto-refreshing if (elapsedTime < autoRefreshDuration) { if (timeoutRef.current) clearTimeout(timeoutRef.current) From 0d729b1f8def902824fbc4f0140db2b14977cbbe Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:07:12 -0700 Subject: [PATCH 23/31] show animation condition --- .../pages/state-managed-bridge/index.tsx | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/synapse-interface/pages/state-managed-bridge/index.tsx b/packages/synapse-interface/pages/state-managed-bridge/index.tsx index ff9cb25d3a..a4ae5100d7 100644 --- a/packages/synapse-interface/pages/state-managed-bridge/index.tsx +++ b/packages/synapse-interface/pages/state-managed-bridge/index.tsx @@ -54,13 +54,11 @@ import { } from '@/slices/transactions/actions' import { useAppDispatch } from '@/store/hooks' import { RootState } from '@/store/store' -import { calculateTimeBetween, getUnixTimeMinutesFromNow } from '@/utils/time' +import { getUnixTimeMinutesFromNow } from '@/utils/time' import { isTransactionReceiptError } from '@/utils/isTransactionReceiptError' import { wagmiConfig } from '@/wagmiConfig' import { useStaleQuoteUpdater } from '@/utils/hooks/useStaleQuoteUpdater' -import { convertUuidToUnix } from '@/utils/convertUuidToUnix' import { useMaintenance } from '@/components/Maintenance/Maintenance' -import { getBridgeModuleNames } from '@/utils/getBridgeModuleNames' import { screenAddress } from '@/utils/screenAddress' import { useWalletState } from '@/slices/wallet/hooks' import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks' @@ -462,15 +460,13 @@ const StateManagedBridge = () => { isBridgePaused={isBridgePaused} isQuoteStale={isQuoteStale} /> - {isConnected && hasValidQuote && ( -
- -
- )} +
+ +
)} From ef5a5046a7167d045996218fb91a2a13199cb860 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:17:38 -0700 Subject: [PATCH 24/31] reset bps threshold --- .../StateManagedBridge/hooks/useConfirmNewBridgePrice.ts | 2 +- packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts index 98e797ba22..ddec669c15 100644 --- a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts @@ -7,7 +7,7 @@ import { BridgeQuote } from '@/utils/types' export const useConfirmNewBridgePrice = () => { const quoteRef = useRef(null) - const bpsThreshold = 0.0000000001 // 1bps + const bpsThreshold = 0.0001 // 1bps const [hasQuoteOutputChanged, setHasQuoteOutputChanged] = useState(false) diff --git a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts index 7f7212d2c5..d7aa8c28a7 100644 --- a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts +++ b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts @@ -31,7 +31,6 @@ export const useStaleQuoteUpdater = ( if (moved && autoRefreshStartTimeRef.current) { autoRefreshStartTimeRef.current = null reset() - console.log('reset autorefresh') } }, [quote]) @@ -45,7 +44,6 @@ export const useStaleQuoteUpdater = ( const elapsedTime = Date.now() - autoRefreshStartTimeRef.current - console.log('elapsedTime: ', elapsedTime) // If autoRefreshDuration hasn't passed, keep auto-refreshing if (elapsedTime < autoRefreshDuration) { if (timeoutRef.current) clearTimeout(timeoutRef.current) From 5c42768d008356ca0964fe68417ff17ff62ad0e0 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:36:54 -0700 Subject: [PATCH 25/31] bridge button conditions --- .../components/StateManagedBridge/BridgeTransactionButton.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx index ea721371a6..eda7260452 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx @@ -73,7 +73,6 @@ export const BridgeTransactionButton = ({ const isButtonDisabled = isBridgePaused || - isQuoteStale || isTyping || isLoading || isWalletPending || @@ -82,6 +81,7 @@ export const BridgeTransactionButton = ({ isBridgeQuoteAmountGreaterThanInputForRfq || (isConnected && !hasValidQuote) || (isConnected && !hasSufficientBalance) || + (isConnected && isQuoteStale) || (destinationAddress && !isAddress(destinationAddress)) let buttonProperties From 10fd34ced51afd3a2d7c7094458539133d895a96 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:59:23 -0700 Subject: [PATCH 26/31] Add new i8n phrases --- .../StateManagedBridge/BridgeTransactionButton.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx index eda7260452..0a18a4ce66 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx @@ -108,12 +108,12 @@ export const BridgeTransactionButton = ({ } } else if (isConnected && !hasSufficientBalance) { buttonProperties = { - label: 'Insufficient balance', + label: t('Insufficient balance'), onClick: null, } } else if (isLoading && hasSameSelectionsAsPreviousQuote) { buttonProperties = { - label: 'Updating quote', + label: t('Updating quote'), onClick: null, } } else if (isLoading) { @@ -183,7 +183,7 @@ export const BridgeTransactionButton = ({ } } else if (hasQuoteOutputChanged && !hasUserConfirmedChange) { buttonProperties = { - label: 'Confirm new quote', + label: t('Confirm new quote'), onClick: () => onUserAcceptChange(), className: '!border !border-synapsePurple !from-bgLight !to-bgLight !animate-pulse', From eea56c7ee81d370e40abd921ea305aa552a6a6e8 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Tue, 10 Sep 2024 18:00:24 -0700 Subject: [PATCH 27/31] rm unused prop --- .../components/buttons/TransactionButton.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/synapse-interface/components/buttons/TransactionButton.tsx b/packages/synapse-interface/components/buttons/TransactionButton.tsx index 9be7c7ad48..e868868dc8 100644 --- a/packages/synapse-interface/components/buttons/TransactionButton.tsx +++ b/packages/synapse-interface/components/buttons/TransactionButton.tsx @@ -19,7 +19,6 @@ export const TransactionButton = ({ onClick, pendingLabel, label, - labelAnimation, onSuccess, disabled, chainId, @@ -30,7 +29,6 @@ export const TransactionButton = ({ onClick: () => Promise pendingLabel: string label: string - labelAnimation?: React.ReactNode onSuccess?: () => void chainId?: number style?: CSSProperties @@ -65,9 +63,7 @@ export const TransactionButton = ({ {pendingLabel}{' '} ) : ( - <> - {label} {labelAnimation} - + <>{label} )} ) From 835003c9d62943c290a84dfcf504b7e74eb12f85 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Wed, 11 Sep 2024 11:31:32 -0700 Subject: [PATCH 28/31] conditions to show countdown animation --- ...matedProgressCircle.tsx => BridgeQuoteResetTimer.tsx} | 6 ++++-- .../StateManagedBridge/BridgeTransactionButton.tsx | 7 ++++++- .../pages/state-managed-bridge/index.tsx | 9 +++++++-- .../utils/hooks/useStaleQuoteUpdater.ts | 7 ++++--- 4 files changed, 21 insertions(+), 8 deletions(-) rename packages/synapse-interface/components/StateManagedBridge/{AnimatedProgressCircle.tsx => BridgeQuoteResetTimer.tsx} (91%) diff --git a/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeQuoteResetTimer.tsx similarity index 91% rename from packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx rename to packages/synapse-interface/components/StateManagedBridge/BridgeQuoteResetTimer.tsx index d3e6eca82c..deedc787af 100644 --- a/packages/synapse-interface/components/StateManagedBridge/AnimatedProgressCircle.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeQuoteResetTimer.tsx @@ -4,14 +4,16 @@ import { useState, useEffect, useMemo } from 'react' export const BridgeQuoteResetTimer = ({ bridgeQuote, hasValidQuote, + isActive, duration, // in ms }: { bridgeQuote: BridgeQuote hasValidQuote: boolean + isActive: boolean duration: number }) => { const memoizedTimer = useMemo(() => { - if (hasValidQuote) { + if (hasValidQuote && isActive) { return ( switchChain({ chainId: fromChainId }), pendingLabel: t('Switching chains'), } - } else if (hasQuoteOutputChanged && !hasUserConfirmedChange) { + } else if ( + hasValidQuote && + hasQuoteOutputChanged && + hasSameSelectionsAsPreviousQuote && + !hasUserConfirmedChange + ) { buttonProperties = { label: t('Confirm new quote'), onClick: () => onUserAcceptChange(), diff --git a/packages/synapse-interface/pages/state-managed-bridge/index.tsx b/packages/synapse-interface/pages/state-managed-bridge/index.tsx index 99267ea387..c099b37ea4 100644 --- a/packages/synapse-interface/pages/state-managed-bridge/index.tsx +++ b/packages/synapse-interface/pages/state-managed-bridge/index.tsx @@ -67,7 +67,7 @@ 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/AnimatedProgressCircle' +import { BridgeQuoteResetTimer } from '@/components/StateManagedBridge/BridgeQuoteResetTimer' import { useBridgeValidations } from '@/components/StateManagedBridge/hooks/useBridgeValidations' const StateManagedBridge = () => { @@ -99,7 +99,7 @@ const StateManagedBridge = () => { const isApproved = useIsBridgeApproved() - const { hasValidQuote } = useBridgeValidations() + const { hasValidQuote, hasSufficientBalance } = useBridgeValidations() const { isWalletPending } = useWalletState() @@ -220,11 +220,15 @@ const StateManagedBridge = () => { } } + const isUpdaterActive = + hasValidQuote && hasSufficientBalance && !isLoading && !isBridgePaused + const isQuoteStale = useStaleQuoteUpdater( bridgeQuote, getAndSetBridgeQuote, isLoading, isWalletPending, + isUpdaterActive, quoteTimeout ) @@ -492,6 +496,7 @@ const StateManagedBridge = () => { diff --git a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts index d7aa8c28a7..3c305c884e 100644 --- a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts +++ b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts @@ -10,8 +10,9 @@ export const useStaleQuoteUpdater = ( refreshQuoteCallback: () => Promise, isQuoteLoading: boolean, isWalletPending: boolean, + isActive: boolean, staleTimeout: number = 15000, // in ms - autoRefreshDuration: number = 60000 // in ms + autoRefreshDuration: number = 30000 // in ms ) => { const [isStale, setIsStale] = useState(false) const eventListenerRef = useRef void)>(null) @@ -34,9 +35,9 @@ export const useStaleQuoteUpdater = ( } }, [quote]) - // Start auto-refresh logic for 60 seconds + // Start auto-refresh logic for autoRefreshDuration seconds useEffect(() => { - if (isValid && !isQuoteLoading && !isWalletPending) { + if (isValid && isActive && !isQuoteLoading && !isWalletPending) { // If auto-refresh has not started yet, initialize the start time if (autoRefreshStartTimeRef.current === null) { autoRefreshStartTimeRef.current = Date.now() From c2c185b7d9d97a5573863e1c0ed16d82d322dbe0 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Wed, 11 Sep 2024 11:36:01 -0700 Subject: [PATCH 29/31] remove pulse effect, add i8n for en-US --- .../StateManagedBridge/BridgeTransactionButton.tsx | 3 +-- packages/synapse-interface/messages/en-US.json | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx index 6eb8fcdc93..5d340f86bb 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx @@ -190,8 +190,7 @@ export const BridgeTransactionButton = ({ buttonProperties = { label: t('Confirm new quote'), onClick: () => onUserAcceptChange(), - className: - '!border !border-synapsePurple !from-bgLight !to-bgLight !animate-pulse', + className: '!border !border-synapsePurple !from-bgLight !to-bgLight', } } else if (!isApproved && hasValidInput && hasValidQuote) { buttonProperties = { diff --git a/packages/synapse-interface/messages/en-US.json b/packages/synapse-interface/messages/en-US.json index 4784ee82d4..998161ba06 100644 --- a/packages/synapse-interface/messages/en-US.json +++ b/packages/synapse-interface/messages/en-US.json @@ -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", From 194799bb942eed202ab0de70b54ba6866375aec6 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Wed, 11 Sep 2024 11:56:26 -0700 Subject: [PATCH 30/31] button outline to prevent shift, disable update when approval required --- .../StateManagedBridge/BridgeQuoteResetTimer.tsx | 6 ++---- .../StateManagedBridge/BridgeTransactionButton.tsx | 3 ++- .../pages/state-managed-bridge/index.tsx | 10 ++++++---- .../utils/hooks/useStaleQuoteUpdater.ts | 8 ++++---- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeQuoteResetTimer.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeQuoteResetTimer.tsx index deedc787af..f8fe334a73 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeQuoteResetTimer.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeQuoteResetTimer.tsx @@ -3,17 +3,15 @@ import { useState, useEffect, useMemo } from 'react' export const BridgeQuoteResetTimer = ({ bridgeQuote, - hasValidQuote, isActive, duration, // in ms }: { bridgeQuote: BridgeQuote - hasValidQuote: boolean isActive: boolean duration: number }) => { const memoizedTimer = useMemo(() => { - if (hasValidQuote && isActive) { + if (isActive) { return ( onUserAcceptChange(), - className: '!border !border-synapsePurple !from-bgLight !to-bgLight', + className: + '!outline !outline-1 !outline-synapsePurple !outline-offset-[-1px] !from-bgLight !to-bgLight', } } else if (!isApproved && hasValidInput && hasValidQuote) { buttonProperties = { diff --git a/packages/synapse-interface/pages/state-managed-bridge/index.tsx b/packages/synapse-interface/pages/state-managed-bridge/index.tsx index c099b37ea4..5247c0aa51 100644 --- a/packages/synapse-interface/pages/state-managed-bridge/index.tsx +++ b/packages/synapse-interface/pages/state-managed-bridge/index.tsx @@ -221,13 +221,16 @@ const StateManagedBridge = () => { } const isUpdaterActive = - hasValidQuote && hasSufficientBalance && !isLoading && !isBridgePaused + hasValidQuote && + hasSufficientBalance && + isApproved && + !isLoading && + !isBridgePaused && + !isWalletPending const isQuoteStale = useStaleQuoteUpdater( bridgeQuote, getAndSetBridgeQuote, - isLoading, - isWalletPending, isUpdaterActive, quoteTimeout ) @@ -495,7 +498,6 @@ const StateManagedBridge = () => {
diff --git a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts index 3c305c884e..0101f7d4cc 100644 --- a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts +++ b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts @@ -8,8 +8,8 @@ import { convertUuidToUnix } from '@/utils/convertUuidToUnix' export const useStaleQuoteUpdater = ( quote: BridgeQuote, refreshQuoteCallback: () => Promise, - isQuoteLoading: boolean, - isWalletPending: boolean, + // isQuoteLoading: boolean, + // isWalletPending: boolean, isActive: boolean, staleTimeout: number = 15000, // in ms autoRefreshDuration: number = 30000 // in ms @@ -37,7 +37,7 @@ export const useStaleQuoteUpdater = ( // Start auto-refresh logic for autoRefreshDuration seconds useEffect(() => { - if (isValid && isActive && !isQuoteLoading && !isWalletPending) { + if (isValid && isActive) { // If auto-refresh has not started yet, initialize the start time if (autoRefreshStartTimeRef.current === null) { autoRefreshStartTimeRef.current = Date.now() @@ -89,7 +89,7 @@ export const useStaleQuoteUpdater = ( } setIsStale(false) } - }, [quote, isQuoteLoading, isWalletPending]) + }, [quote, isActive]) return isStale } From 1112571618ce49397ba281a77d8684ffb1b4e5e6 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Wed, 11 Sep 2024 12:01:25 -0700 Subject: [PATCH 31/31] require approval before confirm modal shows --- .../components/StateManagedBridge/BridgeTransactionButton.tsx | 1 + .../synapse-interface/components/buttons/TransactionButton.tsx | 1 + packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts | 2 -- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx index 14527f4aff..38a1b196bb 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx @@ -182,6 +182,7 @@ export const BridgeTransactionButton = ({ pendingLabel: t('Switching chains'), } } else if ( + isApproved && hasValidQuote && hasQuoteOutputChanged && hasSameSelectionsAsPreviousQuote && diff --git a/packages/synapse-interface/components/buttons/TransactionButton.tsx b/packages/synapse-interface/components/buttons/TransactionButton.tsx index e868868dc8..59baf8755b 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 = ({ diff --git a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts index 0101f7d4cc..0bd8162aaa 100644 --- a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts +++ b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts @@ -8,8 +8,6 @@ import { convertUuidToUnix } from '@/utils/convertUuidToUnix' export const useStaleQuoteUpdater = ( quote: BridgeQuote, refreshQuoteCallback: () => Promise, - // isQuoteLoading: boolean, - // isWalletPending: boolean, isActive: boolean, staleTimeout: number = 15000, // in ms autoRefreshDuration: number = 30000 // in ms