diff --git a/dapp/src/DApp.tsx b/dapp/src/DApp.tsx
index 085b6fbe8..d1d38a8bd 100644
--- a/dapp/src/DApp.tsx
+++ b/dapp/src/DApp.tsx
@@ -2,7 +2,11 @@ import React from "react"
import { ChakraProvider } from "@chakra-ui/react"
import { useDetectThemeMode } from "./hooks"
import theme from "./theme"
-import { LedgerWalletAPIProvider, WalletContextProvider } from "./contexts"
+import {
+ LedgerWalletAPIProvider,
+ StakingFlowProvider,
+ WalletContextProvider,
+} from "./contexts"
import Header from "./components/Header"
import Overview from "./components/Overview"
@@ -23,9 +27,11 @@ function DAppProviders() {
return (
-
-
-
+
+
+
+
+
)
diff --git a/dapp/src/components/Header/index.tsx b/dapp/src/components/Header/index.tsx
index 838ed37b2..d78d97ef8 100644
--- a/dapp/src/components/Header/index.tsx
+++ b/dapp/src/components/Header/index.tsx
@@ -2,9 +2,11 @@ import React from "react"
import { Flex } from "@chakra-ui/react"
import ConnectWallet from "./ConnectWallet"
+export const HEADER_HEIGHT = 24
+
export default function Header() {
return (
-
+
)
diff --git a/dapp/src/components/ModalOverlay/index.tsx b/dapp/src/components/ModalOverlay/index.tsx
new file mode 100644
index 000000000..9ca30d1f1
--- /dev/null
+++ b/dapp/src/components/ModalOverlay/index.tsx
@@ -0,0 +1,41 @@
+import React, { useEffect, useState } from "react"
+import { Box, useColorModeValue } from "@chakra-ui/react"
+import { useStakingFlowContext } from "../../hooks"
+
+const DELAY = 300
+
+export default function ModalOverlay({ marginTop }: { marginTop?: number }) {
+ const { modalType } = useStakingFlowContext()
+ const [showOverlay, setShowOverlay] = useState(!modalType)
+
+ useEffect(() => {
+ const timeout = setTimeout(
+ () => {
+ setShowOverlay(!!modalType)
+ },
+ // When the modal opens, we should show the sidebar without delay.
+ // However, when the modal disappears, the overlay should disappear with some delay.
+ modalType ? 0 : DELAY,
+ )
+ return () => clearTimeout(timeout)
+ }, [modalType])
+
+ return (
+
+ )
+}
diff --git a/dapp/src/components/Modals/BaseModal.tsx b/dapp/src/components/Modals/BaseModal.tsx
new file mode 100644
index 000000000..1f98f4345
--- /dev/null
+++ b/dapp/src/components/Modals/BaseModal.tsx
@@ -0,0 +1,23 @@
+import React from "react"
+import { Modal, ModalCloseButton, ModalContent } from "@chakra-ui/react"
+import { useStakingFlowContext } from "../../hooks"
+import { HEADER_HEIGHT } from "../Header"
+
+export default function BaseModal({ children }: { children: React.ReactNode }) {
+ const { closeModal } = useStakingFlowContext()
+
+ return (
+
+
+
+ {children}
+
+
+ )
+}
diff --git a/dapp/src/components/Modals/ConnectWalletModal.tsx b/dapp/src/components/Modals/ConnectWalletModal.tsx
new file mode 100644
index 000000000..5ca953960
--- /dev/null
+++ b/dapp/src/components/Modals/ConnectWalletModal.tsx
@@ -0,0 +1,44 @@
+import React from "react"
+import {
+ Button,
+ Image,
+ ModalBody,
+ ModalFooter,
+ ModalHeader,
+ VStack,
+ Text,
+} from "@chakra-ui/react"
+import { useRequestBitcoinAccount } from "../../hooks"
+import BaseModal from "./BaseModal"
+import ConnectBTCAccount from "../../static/images/ConnectBTCAccount.png"
+
+export default function ConnectWalletModal() {
+ const { requestAccount } = useRequestBitcoinAccount()
+
+ return (
+
+
+ Bitcoin account not installed
+
+
+
+
+
+ Bitcoin account is required to make transactions for depositing and
+ staking your BTC.
+
+
+
+
+
+
+
+ )
+}
diff --git a/dapp/src/components/Modals/StakingOverviewModal.tsx b/dapp/src/components/Modals/StakingOverviewModal.tsx
new file mode 100644
index 000000000..e7cbcddb2
--- /dev/null
+++ b/dapp/src/components/Modals/StakingOverviewModal.tsx
@@ -0,0 +1,67 @@
+import React from "react"
+import {
+ Button,
+ Flex,
+ ModalBody,
+ ModalFooter,
+ ModalHeader,
+ Step,
+ StepDescription,
+ StepIndicator,
+ StepNumber,
+ StepSeparator,
+ StepTitle,
+ Stepper,
+} from "@chakra-ui/react"
+import { useStakingFlowContext } from "../../hooks"
+import BaseModal from "./BaseModal"
+
+const STEPS = [
+ {
+ title: "Sign message",
+ description:
+ "You will sign a gas-free Ethereum message to indicate the address where you'd like to get your stBTC liquid staking token.",
+ },
+ {
+ title: "Deposit BTC",
+ description:
+ "You will make a Bitcoin transaction to deposit and stake your BTC.",
+ },
+]
+
+export default function StakingOverviewModal() {
+ const { closeModal } = useStakingFlowContext()
+
+ return (
+
+ Staking overview
+
+
+ {STEPS.map((step) => (
+
+
+
+
+
+ {step.title}
+ {step.description}
+
+
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/dapp/src/components/Overview/PositionDetails.tsx b/dapp/src/components/Overview/PositionDetails.tsx
index 972db661f..cfe51119c 100644
--- a/dapp/src/components/Overview/PositionDetails.tsx
+++ b/dapp/src/components/Overview/PositionDetails.tsx
@@ -10,34 +10,41 @@ import {
} from "@chakra-ui/react"
import { BITCOIN, USD } from "../../constants"
import { Info } from "../../static/icons"
+import Staking from "../Staking"
+import { useStakingFlowContext } from "../../hooks"
export default function PositionDetails() {
+ const { setModalType } = useStakingFlowContext()
+
return (
-
-
- Your positions
-
-
- 34.75
- {BITCOIN.symbol}
-
-
+ <>
+
+
+ Your positions
+
- 1.245.148,1
- {USD.symbol}
+ 34.75
+ {BITCOIN.symbol}
- {/* TODO: Add correct text for tooltip */}
-
-
-
+
+
+ 1.245.148,1
+ {USD.symbol}
+
+ {/* TODO: Add correct text for tooltip */}
+
+
+
+
+
+ {/* TODO: Handle click actions */}
+
+
+
-
- {/* TODO: Handle click actions */}
-
-
-
-
+
+ >
)
}
diff --git a/dapp/src/components/Staking/index.tsx b/dapp/src/components/Staking/index.tsx
new file mode 100644
index 000000000..b4e56103b
--- /dev/null
+++ b/dapp/src/components/Staking/index.tsx
@@ -0,0 +1,28 @@
+import React from "react"
+import ConnectWalletModal from "../Modals/ConnectWalletModal"
+import StakingOverviewModal from "../Modals/StakingOverviewModal"
+import { useStakingFlowContext, useWalletContext } from "../../hooks"
+import ModalOverlay from "../ModalOverlay"
+import { HEADER_HEIGHT } from "../Header"
+
+function Modal() {
+ const { modalType } = useStakingFlowContext()
+ const { btcAccount } = useWalletContext()
+
+ if (!modalType) return null
+
+ if (!btcAccount) return
+
+ if (modalType === "overview") return
+}
+
+export default function Staking() {
+ return (
+ <>
+
+ {/* The user has several modals in a flow.
+ Let's use our own modal overlay to prevent the background flickering effect. */}
+
+ >
+ )
+}
diff --git a/dapp/src/contexts/StakingFlowContext.tsx b/dapp/src/contexts/StakingFlowContext.tsx
new file mode 100644
index 000000000..a58446787
--- /dev/null
+++ b/dapp/src/contexts/StakingFlowContext.tsx
@@ -0,0 +1,42 @@
+import React, { createContext, useCallback, useMemo, useState } from "react"
+import { ModalType } from "../types"
+
+type StakingFlowContextValue = {
+ modalType: ModalType | undefined
+ closeModal: () => void
+ setModalType: React.Dispatch>
+}
+
+export const StakingFlowContext = createContext({
+ modalType: undefined,
+ setModalType: () => {},
+ closeModal: () => {},
+})
+
+export function StakingFlowProvider({
+ children,
+}: {
+ children: React.ReactNode
+}): React.ReactElement {
+ const [modalType, setModalType] = useState(undefined)
+
+ const closeModal = useCallback(() => {
+ setModalType(undefined)
+ }, [])
+
+ const contextValue: StakingFlowContextValue =
+ useMemo(
+ () => ({
+ modalType,
+ closeModal,
+ setModalType,
+ }),
+ [modalType, closeModal],
+ )
+
+ return (
+
+ {children}
+
+ )
+}
diff --git a/dapp/src/contexts/WalletContext.tsx b/dapp/src/contexts/WalletContext.tsx
index f327ce0bd..63fb3faba 100644
--- a/dapp/src/contexts/WalletContext.tsx
+++ b/dapp/src/contexts/WalletContext.tsx
@@ -8,9 +8,12 @@ type WalletContextValue = {
setEthAccount: React.Dispatch>
}
-export const WalletContext = createContext(
- undefined,
-)
+export const WalletContext = createContext({
+ ethAccount: undefined,
+ btcAccount: undefined,
+ setEthAccount: () => {},
+ setBtcAccount: () => {},
+})
export function WalletContextProvider({
children,
@@ -27,7 +30,7 @@ export function WalletContextProvider({
ethAccount,
setEthAccount,
}),
- [btcAccount, setBtcAccount, ethAccount, setEthAccount],
+ [btcAccount, ethAccount],
)
return (
diff --git a/dapp/src/contexts/index.tsx b/dapp/src/contexts/index.tsx
index 3ea3058ed..9271773c0 100644
--- a/dapp/src/contexts/index.tsx
+++ b/dapp/src/contexts/index.tsx
@@ -1,2 +1,3 @@
export * from "./WalletContext"
export * from "./LedgerWalletAPIProvider"
+export * from "./StakingFlowContext"
diff --git a/dapp/src/hooks/index.ts b/dapp/src/hooks/index.ts
index 038388476..b12025abb 100644
--- a/dapp/src/hooks/index.ts
+++ b/dapp/src/hooks/index.ts
@@ -2,3 +2,4 @@ export * from "./useDetectThemeMode"
export * from "./useRequestBitcoinAccount"
export * from "./useRequestEthereumAccount"
export * from "./useWalletContext"
+export * from "./useStakingFlowContext"
diff --git a/dapp/src/hooks/useRequestBitcoinAccount.ts b/dapp/src/hooks/useRequestBitcoinAccount.ts
index 971a3f9ef..b9cce07c2 100644
--- a/dapp/src/hooks/useRequestBitcoinAccount.ts
+++ b/dapp/src/hooks/useRequestBitcoinAccount.ts
@@ -5,12 +5,12 @@ import { UseRequestAccountReturn } from "../types"
import { WalletContext } from "../contexts"
export function useRequestBitcoinAccount(): UseRequestAccountReturn {
- const walletContext = useContext(WalletContext)
+ const { setBtcAccount } = useContext(WalletContext)
const { account, requestAccount } = useRequestAccount()
useEffect(() => {
- walletContext?.setBtcAccount(account || undefined)
- }, [account, walletContext])
+ setBtcAccount(account || undefined)
+ }, [account, setBtcAccount])
const requestBitcoinAccount = useCallback(async () => {
await requestAccount({ currencyIds: [CURRENCY_ID_BITCOIN] })
diff --git a/dapp/src/hooks/useRequestEthereumAccount.ts b/dapp/src/hooks/useRequestEthereumAccount.ts
index ebeb1f268..c780aea20 100644
--- a/dapp/src/hooks/useRequestEthereumAccount.ts
+++ b/dapp/src/hooks/useRequestEthereumAccount.ts
@@ -5,12 +5,12 @@ import { UseRequestAccountReturn } from "../types"
import { WalletContext } from "../contexts"
export function useRequestEthereumAccount(): UseRequestAccountReturn {
- const walletContext = useContext(WalletContext)
+ const { setEthAccount } = useContext(WalletContext)
const { account, requestAccount } = useRequestAccount()
useEffect(() => {
- walletContext?.setEthAccount(account || undefined)
- }, [account, walletContext])
+ setEthAccount(account || undefined)
+ }, [account, setEthAccount])
const requestEthereumAccount = useCallback(async () => {
await requestAccount({ currencyIds: [CURRENCY_ID_ETHEREUM] })
diff --git a/dapp/src/hooks/useStakingFlowContext.ts b/dapp/src/hooks/useStakingFlowContext.ts
new file mode 100644
index 000000000..870e15f71
--- /dev/null
+++ b/dapp/src/hooks/useStakingFlowContext.ts
@@ -0,0 +1,14 @@
+import { useContext } from "react"
+import { StakingFlowContext } from "../contexts"
+
+export function useStakingFlowContext() {
+ const context = useContext(StakingFlowContext)
+
+ if (!context) {
+ throw new Error(
+ "StakingFlowContext used outside of StakingFlowContext component",
+ )
+ }
+
+ return context
+}
diff --git a/dapp/src/static/images/ConnectBTCAccount.png b/dapp/src/static/images/ConnectBTCAccount.png
new file mode 100644
index 000000000..deff3c0cc
Binary files /dev/null and b/dapp/src/static/images/ConnectBTCAccount.png differ
diff --git a/dapp/src/types/index.ts b/dapp/src/types/index.ts
index 1e77e81e7..32b259c7f 100644
--- a/dapp/src/types/index.ts
+++ b/dapp/src/types/index.ts
@@ -1,2 +1,3 @@
export * from "./ledger-live-app"
export * from "./currency"
+export * from "./staking"
diff --git a/dapp/src/types/staking.ts b/dapp/src/types/staking.ts
new file mode 100644
index 000000000..38c5d8098
--- /dev/null
+++ b/dapp/src/types/staking.ts
@@ -0,0 +1 @@
+export type ModalType = "overview" | "stake"