Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(synapse-interface): bridge quote uuid #2900

Merged
merged 30 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
58a0a25
feat: implement uuid per `BridgeQuote`
bigboydiamonds Jul 18, 2024
d16f30c
feat: validateUUID + tests
bigboydiamonds Jul 18, 2024
54ed56d
test: add sdk test for uuid
bigboydiamonds Jul 19, 2024
e1cc878
Add `id` field to `type BridgeQuote` for UUID
bigboydiamonds Jul 19, 2024
54bcd1e
Track uuid in analytics
bigboydiamonds Jul 19, 2024
af9f372
Convert uuid to unix util
bigboydiamonds Jul 20, 2024
1d3fa9e
Refresh quote before executing bridge transaction if stale
bigboydiamonds Jul 20, 2024
646c329
Add `executeBridge()` check additional to stale quote refresher
bigboydiamonds Jul 22, 2024
1196773
Clean button and logs
bigboydiamonds Jul 22, 2024
669734c
Define quote timeout for hook + callback
bigboydiamonds Jul 22, 2024
b652748
Fix es module issue with uuidv7 pkg
bigboydiamonds Jul 23, 2024
ca792a4
Add jest to fix cwd error
bigboydiamonds Jul 23, 2024
4e99d8c
Lint
bigboydiamonds Jul 23, 2024
ee7d9ce
yarn install
bigboydiamonds Jul 24, 2024
bc74896
babel-jest
bigboydiamonds Jul 24, 2024
923bf54
Match jest and babel-jest to fix cwd
bigboydiamonds Jul 24, 2024
b7f53c3
Update babel-jest versioning
bigboydiamonds Jul 24, 2024
21b5155
Merge branch 'sdk/bridge-quote-uuid' into fe/bridge-quote-uuid
bigboydiamonds Jul 24, 2024
d750399
test: different bridge quotes contain unique ids
bigboydiamonds Jul 29, 2024
c8b350c
test: unique IDs for quotes generated in `sdk.allBridgeQuotes()` call
bigboydiamonds Jul 29, 2024
245807e
Refetch quote in bridge callback if not already fetching, rename time…
bigboydiamonds Jul 30, 2024
1cdca09
Merge branch 'master' into sdk/bridge-quote-uuid
bigboydiamonds Aug 7, 2024
4bc1a1e
test uuid to be valid string
bigboydiamonds Aug 23, 2024
acd3091
Merge branch 'sdk/bridge-quote-uuid' into fe/bridge-quote-uuid
bigboydiamonds Aug 26, 2024
642a506
Merge branch 'master' into fe/bridge-quote-uuid
bigboydiamonds Aug 26, 2024
1756d6d
Fix package.json spacing
bigboydiamonds Aug 26, 2024
04716d4
remove refetch in callback
bigboydiamonds Aug 29, 2024
72f9756
remove address field; already tracked in segment provider
bigboydiamonds Aug 29, 2024
5d526c4
Merge branch 'master' into fe/bridge-quote-uuid
abtestingalpha Aug 29, 2024
ef838ad
Removes address
abtestingalpha Aug 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
bigboydiamonds marked this conversation as resolved.
Show resolved Hide resolved
}
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
Loading