Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
143 changes: 143 additions & 0 deletions src/components/Modal/tBTC/InitiateBridging.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import {
BodyLg,
BodySm,
Button,
Divider,
H5,
List,
ModalBody,
ModalFooter,
ModalHeader,
Skeleton,
Box,
} from "@threshold-network/components"
import { FC } from "react"
import { BaseModalProps } from "../../../types"
import InfoBox from "../../InfoBox"
import { InlineTokenBalance } from "../../TokenBalance"
import {
TransactionDetailsAmountItem,
TransactionDetailsItem,
} from "../../TransactionDetails"
import ModalCloseButton from "../ModalCloseButton"
import withBaseModal from "../withBaseModal"
import { PosthogButtonId } from "../../../types/posthog"
import SubmitTxButton from "../../SubmitTxButton"
import { BridgeRoute } from "../../../threshold-ts/bridge"
import { BigNumber } from "ethers"
import { formatUnits } from "@ethersproject/units"

type InitiateBridgingProps = {
amount: string
fromNetwork: string
toNetwork: string
bridgeRoute: BridgeRoute
estimatedFee: BigNumber | null
estimatedTime: number
onConfirm: () => Promise<void>
} & BaseModalProps

const InitiateBridgingBase: FC<InitiateBridgingProps> = ({
closeModal,
amount,
fromNetwork,
toNetwork,
bridgeRoute,
estimatedFee,
estimatedTime,
onConfirm,
}) => {
const formatTime = (seconds: number): string => {
if (seconds < 3600) {
return `~${Math.round(seconds / 60)} minutes`
} else if (seconds < 86400) {
return `~${Math.round(seconds / 3600)} hours`
} else {
return `~${Math.round(seconds / 86400)} days`
}
}

const handleConfirm = () => {
try {
// Close immediately, run in background
closeModal()
void onConfirm()
} catch (error) {
console.error("Bridge transaction failed:", error)
}
}

const bridgeTypeText =
bridgeRoute === "ccip" ? "CCIP Bridge" : "Standard Bridge"
const feeAmount = estimatedFee ? formatUnits(estimatedFee, 18) : "0"

return (
<>
<ModalHeader>Initiate {bridgeTypeText}</ModalHeader>
<ModalCloseButton />
<ModalBody>
<InfoBox variant="modal" mb="6">
<H5>
You are bridging{" "}
<InlineTokenBalance
tokenSymbol="tBTC"
tokenAmount={amount}
withSymbol
precision={6}
higherPrecision={8}
/>
</H5>
<BodyLg mt="4">
From {fromNetwork} to {toNetwork} using {bridgeTypeText}. This will
take approximately {formatTime(estimatedTime)}.
</BodyLg>
</InfoBox>
<List mt="6" spacing="2">
<TransactionDetailsAmountItem
label="Bridge Amount"
amount={amount}
suffixItem="tBTC"
precision={6}
higherPrecision={8}
/>
<TransactionDetailsAmountItem
label="Estimated Fee"
amount={feeAmount}
suffixItem="ETH"
precision={6}
higherPrecision={8}
/>
<TransactionDetailsItem label="From Network" value={fromNetwork} />
<TransactionDetailsItem label="To Network" value={toNetwork} />
<TransactionDetailsItem label="Bridge Type" value={bridgeTypeText} />
<TransactionDetailsItem
label="Estimated Time"
value={formatTime(estimatedTime)}
/>
</List>
<BodySm textAlign="center" mt="9">
{bridgeRoute === "ccip" ? (
<>
Your tokens will be bridged using Chainlink CCIP. The transaction
will be viewable on the CCIP Explorer.
</>
) : (
<>
Your tokens will be bridged using the Standard Bridge. This
process takes 7 days to complete.
</>
)}
</BodySm>
<Divider mt="4" />
</ModalBody>
<ModalFooter>
<Button onClick={closeModal} variant="outline" mr={2}>
Cancel
</Button>
<SubmitTxButton onSubmit={handleConfirm}>Confirm Bridge</SubmitTxButton>
</ModalFooter>
</>
)
}

export const InitiateBridging = withBaseModal(InitiateBridgingBase)
1 change: 1 addition & 0 deletions src/components/Modal/tBTC/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./NewTBTCApp"
export * from "./GenerateNewDepositAddress"
export * from "./InitiateUnminting"
export * from "./InitiateBridging"
24 changes: 16 additions & 8 deletions src/components/Navbar/NetworkButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,28 @@ const getNetworkIcon = (chainId: number, colorMode: string): NetworkIconMap => {
icon: <Icon as={Arbitrum} boxSize="5" />,
bg: grayBackground,
},
[SupportedChainIds.Bob]: {
icon: <Icon as={Arbitrum} boxSize="5" />,
bg: grayBackground,
},
}
: {
[SupportedChainIds.Sepolia]: {
icon: <Icon as={ethereumLogo} boxSize="5" />,
bg: grayBackground,
},
// [SupportedChainIds.ArbitrumSepolia]: {
// icon: <Icon as={Arbitrum} boxSize="5" />,
// bg: grayBackground,
// },
// [SupportedChainIds.BaseSepolia]: {
// icon: <Icon as={Base} boxSize="5" />,
// bg: "blue.500",
// },
[SupportedChainIds.ArbitrumSepolia]: {
icon: <Icon as={Arbitrum} boxSize="5" />,
bg: grayBackground,
},
[SupportedChainIds.BaseSepolia]: {
icon: <Icon as={Base} boxSize="5" />,
bg: "blue.500",
},
[SupportedChainIds.BobSepolia]: {
icon: <Icon as={Arbitrum} boxSize="5" />,
bg: grayBackground,
},
}),
}

Expand Down
9 changes: 4 additions & 5 deletions src/components/SubNavigationPills/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ const getPathMatches = (pills: RouteProps[], locationPathname: string) => {
: `${parentPathBase}/${path}`
)
const match = matchPath(
{ path: resolved.pathname, end: true },
{ path: resolved.pathname, end: false },
currentPathname
)
pathMatches.push({
Expand All @@ -168,16 +168,15 @@ const getActivePillIndex = (pills: RouteProps[], locationPathname: string) => {
if (matchedPaths.length === 0) return undefined
if (matchedPaths.length === 1) return matchedPaths[0].index

const matchedElementWithLongestPathnameBase = matchedPaths.reduce(
const matchedElementWithLongestPath = matchedPaths.reduce(
(maxElement, currentElement) => {
return currentElement.match.pathnameBase.length >
maxElement.match.pathnameBase.length
return currentElement.resolvedPath.length > maxElement.resolvedPath.length
? currentElement
: maxElement
}
)

return matchedElementWithLongestPathnameBase.index
return matchedElementWithLongestPath.index
}

export default SubNavigationPills
62 changes: 44 additions & 18 deletions src/components/tBTC/BridgeActivity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,23 @@ import { useIsActive } from "../../hooks/useIsActive"
import { useNonEVMConnection } from "../../hooks/useNonEVMConnection"

export type BridgeActivityProps = {
data: BridgeActivityType[]
data: (BridgeActivityType | BridgeActivityTypeExtended)[]
isFetching: boolean
emptyState?: ReactElement
}

// Extended type to support bridge activities
export type BridgeActivityTypeExtended = BridgeActivityType & {
additionalData?: {
bridgeRoute?: string
fromNetwork?: string
toNetwork?: string
explorerUrl?: string
redeemerOutputScript?: string
walletPublicKeyHash?: string
}
}

type BridgeActivityContextValue = {
[Property in keyof BridgeActivityProps]-?: BridgeActivityProps[Property]
} & { isBridgeHistoryEmpty: boolean }
Expand Down Expand Up @@ -101,7 +113,7 @@ export const BridgeActivityData: FC<ListProps> = (props) => {
)
}

const ActivityItem: FC<BridgeActivityType> = ({
const ActivityItem: FC<BridgeActivityTypeExtended> = ({
amount,
status,
activityKey,
Expand All @@ -111,20 +123,28 @@ const ActivityItem: FC<BridgeActivityType> = ({
}) => {
const { account } = useIsActive()

const link =
bridgeProcess === "unmint"
? RedemptionDetailsLinkBuilder.createFromTxHash(txHash)
.withRedeemer(account!)
.withRedeemerOutputScript(
(additionalData as UnmintBridgeActivityAdditionalData)
.redeemerOutputScript
)
.withWalletPublicKeyHash(
(additionalData as UnmintBridgeActivityAdditionalData)
.walletPublicKeyHash
)
.build()
: `/tBTC/mint/deposit/${activityKey}`
let link: string
let isExternal = false

// Check if this is a bridge activity with explorerUrl
if (additionalData?.explorerUrl) {
link = additionalData.explorerUrl
isExternal = true
} else if (bridgeProcess === "unmint") {
link = RedemptionDetailsLinkBuilder.createFromTxHash(txHash)
.withRedeemer(account!)
.withRedeemerOutputScript(
(additionalData as UnmintBridgeActivityAdditionalData)
.redeemerOutputScript
)
.withWalletPublicKeyHash(
(additionalData as UnmintBridgeActivityAdditionalData)
.walletPublicKeyHash
)
.build()
} else {
link = `/tBTC/deposit/mint/deposit/${activityKey}`
}

return (
<ActivityItemWrapper>
Expand All @@ -134,6 +154,7 @@ const ActivityItem: FC<BridgeActivityType> = ({
_hover={{ textDecoration: "none" }}
color="inherit"
to={link}
isExternal={isExternal}
>
<InlineTokenBalance tokenAmount={amount} />
</LinkOverlay>
Expand All @@ -142,8 +163,13 @@ const ActivityItem: FC<BridgeActivityType> = ({
)
}

const renderActivityItem = (item: BridgeActivityType) => (
<ActivityItem key={`${item.activityKey}-${item.txHash}`} {...item} />
const renderActivityItem = (
item: BridgeActivityType | BridgeActivityTypeExtended
) => (
<ActivityItem
key={`${item.activityKey}-${item.txHash}`}
{...(item as BridgeActivityTypeExtended)}
/>
)

const bridgeActivityStatusToBadgeProps: Record<
Expand Down
1 change: 1 addition & 0 deletions src/enums/modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,5 @@ export enum ModalType {
InitiateUnminting = "INITIATE_UNMINTING",
TACoCommitment = "TACO_COMMITMENT",
TACoCommitmentSuccess = "TACO_COMMITMENT_SUCCESS",
InitiateBridging = "INITIATE_BRIDGING",
}
2 changes: 2 additions & 0 deletions src/hooks/tbtc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ export * from "./useTBTCVaultContract"
export * from "./useSubscribeToRedemptionsCompletedEvent"
export * from "./useFindRedemptionInBitcoinTx"
export * from "./useStarknetTBTCBalance"
export * from "./useBridge"
export * from "./useBridgeActivity"
Loading