Skip to content

Commit

Permalink
feat(synapse-interface): bridge quote uuid (#2900)
Browse files Browse the repository at this point in the history
* feat: implement uuid per `BridgeQuote`

* Convert uuid to unix util


---------

Co-authored-by: abtestingalpha <[email protected]>
  • Loading branch information
bigboydiamonds and abtestingalpha authored Aug 29, 2024
1 parent eccbfd1 commit b568cbe
Show file tree
Hide file tree
Showing 14 changed files with 63 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export const BridgeTransactionButton = ({
} else if (isLoading) {
buttonProperties = {
label: `Bridge ${fromToken?.symbol}`,
pendingLabel: `Bridge ${fromToken?.symbol}`,
onClick: null,
}
} else if (!isConnected && hasValidInput) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { memo } from 'react'
import { getTimeMinutesBeforeNow } from '@/utils/time'
import { getUnixTimeMinutesBeforeNow } from '@/utils/time'

/**
* @param {string} id - The unique ID of a rendered instance.
Expand All @@ -19,7 +19,7 @@ export const AnimatedProgressBar = memo(
estDuration: number
status: 'pending' | 'completed' | 'reverted'
}) => {
const currentTime = getTimeMinutesBeforeNow(0)
const currentTime = getUnixTimeMinutesBeforeNow(0)
const elapsedTime = currentTime - startTime
const remainingTime = estDuration - elapsedTime
const percentElapsed = (elapsedTime / estDuration) * 100
Expand Down
1 change: 1 addition & 0 deletions packages/synapse-interface/constants/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as CHAINS from '@constants/chains/master'
export const QUOTE_POLLING_INTERVAL = 10000

export const EMPTY_BRIDGE_QUOTE = {
id: '',
inputAmountForQuote: '',
originTokenForQuote: null,
destTokenForQuote: null,
Expand Down
1 change: 1 addition & 0 deletions packages/synapse-interface/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"tailwindcss-border-gradient-radius": "^3.0.1",
"typescript": "5.1.6",
"use-persisted-state": "^0.3.3",
"uuidv7": "^1.0.1",
"viem": "^2.13.6",
"wagmi": "^2.9.8",
"yarn": "^1.22.19"
Expand Down
26 changes: 16 additions & 10 deletions packages/synapse-interface/pages/state-managed-bridge/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,13 @@ import {
} from '@/slices/transactions/actions'
import { useAppDispatch } from '@/store/hooks'
import { RootState } from '@/store/store'
import { getTimeMinutesFromNow } from '@/utils/time'
import { calculateTimeBetween, getUnixTimeMinutesFromNow } from '@/utils/time'
import { isTransactionReceiptError } from '@/utils/isTransactionReceiptError'
import { useMaintenance } from '@/components/Maintenance/Maintenance'
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'
Expand All @@ -67,6 +69,7 @@ import { fetchBridgeQuote } from '@/slices/bridgeQuote/thunks'
import { useIsBridgeApproved } from '@/utils/hooks/useIsBridgeApproved'

const StateManagedBridge = () => {
const dispatch = useAppDispatch()
const { address } = useAccount()
const { synapseSDK } = useSynapseContext()
const router = useRouter()
Expand All @@ -75,6 +78,7 @@ const StateManagedBridge = () => {
const bridgeDisplayRef = useRef(null)
const currentSDKRequestID = useRef(0)
const quoteToastRef = useRef({ id: '' })
const quoteTimeout = 15000

const [isTyping, setIsTyping] = useState(false)

Expand Down Expand Up @@ -104,8 +108,6 @@ const StateManagedBridge = () => {
BridgeMaintenanceWarningMessage,
} = useMaintenance()

const dispatch = useAppDispatch()

useEffect(() => {
segmentAnalyticsEvent(`[Bridge page] arrives`, {
fromChainId,
Expand Down Expand Up @@ -136,7 +138,7 @@ const StateManagedBridge = () => {

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

try {
if (thisRequestId === currentSDKRequestID.current) {
Expand Down Expand Up @@ -200,7 +202,8 @@ const StateManagedBridge = () => {
bridgeQuote,
getAndSetBridgeQuote,
isLoading,
isWalletPending
isWalletPending,
quoteTimeout
)

const approveTxn = async () => {
Expand All @@ -225,6 +228,8 @@ const StateManagedBridge = () => {
const executeBridge = async () => {
let pendingPopup: any

const currentTimestamp: number = getUnixTimeMinutesFromNow(0)

if (destinationAddress) {
const isRisky = await screenAddress(destinationAddress)
if (isRisky) {
Expand All @@ -235,6 +240,7 @@ const StateManagedBridge = () => {
segmentAnalyticsEvent(
`[Bridge] initiates bridge`,
{
id: bridgeQuote.id,
originChainId: fromChainId,
destinationChainId: toChainId,
inputAmount: debouncedFromValue,
Expand All @@ -248,7 +254,7 @@ const StateManagedBridge = () => {
},
true
)
const currentTimestamp: number = getTimeMinutesFromNow(0)

dispatch(
addPendingBridgeTransaction({
id: currentTimestamp,
Expand Down Expand Up @@ -314,6 +320,7 @@ const StateManagedBridge = () => {
{ id: 'bridge-in-progress-popup', duration: Infinity }
)
segmentAnalyticsEvent(`[Bridge] bridges successfully`, {
id: bridgeQuote.id,
originChainId: fromChainId,
destinationChainId: toChainId,
inputAmount: debouncedFromValue,
Expand Down Expand Up @@ -358,11 +365,10 @@ const StateManagedBridge = () => {

toast.dismiss(pendingPopup)

const transactionReceipt = await waitForTransactionReceipt(wagmiConfig, {
await waitForTransactionReceipt(wagmiConfig, {
hash: tx as Address,
timeout: 60_000,
})
console.log('Transaction Receipt: ', transactionReceipt)

/** Update Origin Chain token balances after resolved tx or timeout reached */
/** Assume tx has been actually resolved if above times out */
Expand All @@ -379,7 +385,7 @@ const StateManagedBridge = () => {
errorCode: error.code,
})
dispatch(removePendingBridgeTransaction(currentTimestamp))
console.log('Error executing bridge', error)
console.error('Error executing bridge: ', error)
toast.dismiss(pendingPopup)

/** Fetch balances if await transaction receipt times out */
Expand Down
2 changes: 2 additions & 0 deletions packages/synapse-interface/slices/bridgeQuote/thunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export const fetchBridgeQuote = createAsyncThunk(
}

const {
id,
feeAmount,
routerAddress,
maxAmountOut,
Expand Down Expand Up @@ -184,6 +185,7 @@ export const fetchBridgeQuote = createAsyncThunk(
originChainId,
destChainId,
requestId,
id,
}
}
)
22 changes: 22 additions & 0 deletions packages/synapse-interface/utils/convertUuidToUnix.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { UUID } from 'uuidv7'

export const convertUuidToUnix = (uuid: string) => {
try {
const timestampBytes = new Uint8Array(8)
timestampBytes.set(
new Uint8Array(UUID.parse(uuid).bytes.buffer.slice(0, 6)),
2
)
const timestampMs = new DataView(timestampBytes.buffer).getBigUint64(
0,
false
)

const unixTimestamp = Number(timestampMs) / 1000

return unixTimestamp
} catch (e) {
console.error('Invalid uuid', e)
return null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from '@/slices/application/actions'
import { useAppDispatch } from '@/store/hooks'
import { isValidAddress, getValidAddress } from '@/utils/isValidAddress'
import { getTimeMinutesBeforeNow } from '@/utils/time'
import { getUnixTimeMinutesBeforeNow } from '@/utils/time'
import { resetTransactionsState } from '@/slices/transactions/actions'

export const useApplicationListener = () => {
Expand All @@ -27,7 +27,7 @@ export const useApplicationListener = () => {
useEffect(() => {
if (lastConnectedTimestamp) {
const sevenDaysInSeconds = 7 * 24 * 60 * 60
const sevenDaysAgo: number = getTimeMinutesBeforeNow(10080)
const sevenDaysAgo: number = getUnixTimeMinutesBeforeNow(10080)

if (sevenDaysAgo - lastConnectedTimestamp > sevenDaysInSeconds) {
console.log('reset cache from < 7 days stale')
Expand All @@ -42,9 +42,9 @@ export const useApplicationListener = () => {
}

dispatch(updateLastConnectedAddress(address))
dispatch(updateLastConnectedTime(getTimeMinutesBeforeNow(0)))
dispatch(updateLastConnectedTime(getUnixTimeMinutesBeforeNow(0)))
} else {
dispatch(updateLastConnectedTime(getTimeMinutesBeforeNow(0)))
dispatch(updateLastConnectedTime(getUnixTimeMinutesBeforeNow(0)))
}
}, [address, lastConnectedAddress, lastConnectedTimestamp])

Expand Down
6 changes: 3 additions & 3 deletions packages/synapse-interface/utils/hooks/useIntervalTimer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useState, useEffect } from 'react'

import { getTimeMinutesFromNow } from '@/utils/time'
import { getUnixTimeMinutesFromNow } from '@/utils/time'

/**
* Hook for setting an interval based timer
Expand All @@ -14,14 +14,14 @@ export const useIntervalTimer = (
isDisabled?: boolean
) => {
const [currentTime, setCurrentTime] = useState<number>(
getTimeMinutesFromNow(0)
getUnixTimeMinutesFromNow(0)
)

/** Update time at set intervals if not disabled */
useEffect(() => {
if (!isDisabled) {
const interval = setInterval(() => {
const newCurrentTime = getTimeMinutesFromNow(0)
const newCurrentTime = getUnixTimeMinutesFromNow(0)
setCurrentTime(newCurrentTime)
}, intervalInMs)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useEffect, useRef } from 'react'
import { BridgeQuote } from '@/utils/types'
import { calculateTimeBetween } from '@/utils/time'
import { useIntervalTimer } from '@/utils/hooks/useIntervalTimer'
import { convertUuidToUnix } from '@/utils/convertUuidToUnix'

/**
* Refreshes quotes based on selected stale timeout duration.
Expand All @@ -14,12 +15,14 @@ export const useStaleQuoteUpdater = (
refreshQuoteCallback: () => Promise<void>,
isQuoteLoading: boolean,
isWalletPending: boolean,
staleTimeout: number = 15000 // 15_000ms or 15s
staleTimeout: number = 15000 // Default 15_000ms or 15s
) => {
const quoteTime = quote?.timestamp
const eventListenerRef = useRef<null | (() => void)>(null)

const quoteTime = quote?.id ? convertUuidToUnix(quote?.id) : null
const isValidQuote = isNumber(quoteTime) && !isNull(quoteTime)

const currentTime = useIntervalTimer(staleTimeout, !isValidQuote)
const eventListenerRef = useRef<null | (() => void)>(null)

useEffect(() => {
if (isValidQuote && !isQuoteLoading && !isWalletPending) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import { useLazyGetUserHistoricalActivityQuery } from '@/slices/api/generated'
import { useTransactionsState } from '@/slices/transactions/hooks'
import { TransactionsState } from '@/slices/transactions/reducer'
import { useAppDispatch } from '@/store/hooks'
import { getTimeMinutesBeforeNow, oneMonthInMinutes } from '@/utils/time'
import { getUnixTimeMinutesBeforeNow, oneMonthInMinutes } from '@/utils/time'

const queryHistoricalTime: number = getTimeMinutesBeforeNow(oneMonthInMinutes)
const queryHistoricalTime: number = getUnixTimeMinutesBeforeNow(oneMonthInMinutes)
// const queryPendingTime: number = getTimeMinutesBeforeNow(oneDayInMinutes)

const POLLING_INTERVAL: number = 300000 // 5 minutes in ms
Expand Down
4 changes: 2 additions & 2 deletions packages/synapse-interface/utils/time.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
export const oneMonthInMinutes: number = 43200
export const oneDayInMinutes: number = 1440

export const getTimeMinutesFromNow = (minutesFromNow) => {
export const getUnixTimeMinutesFromNow = (minutesFromNow) => {
const currentTimeSeconds = new Date().getTime() / 1000

return Math.round(currentTimeSeconds + 60 * minutesFromNow)
}

export const getTimeMinutesBeforeNow = (minutesBeforeNow) => {
export const getUnixTimeMinutesBeforeNow = (minutesBeforeNow) => {
const currentTimeSeconds = new Date().getTime() / 1000

return Math.round(currentTimeSeconds - 60 * minutesBeforeNow)
Expand Down
2 changes: 0 additions & 2 deletions packages/synapse-interface/utils/txErrorHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ function checkStringForNotEnoughGas(str: string) {
}

export const txErrorHandler = (err: any) => {
console.log('err from txErrorHandler: ', err)

if (err?.details && checkStringForRejection(err?.details)) {
return toast.error('User denied transaction', {
id: 'toast-error-user-reject',
Expand Down
1 change: 1 addition & 0 deletions packages/synapse-interface/utils/types/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ type QuoteQuery = {
}

export type BridgeQuote = {
id: string
inputAmountForQuote: string
originTokenForQuote: Token
destTokenForQuote: Token
Expand Down

0 comments on commit b568cbe

Please sign in to comment.