diff --git a/dapp/.env b/dapp/.env
deleted file mode 100644
index fdb71b35d..000000000
--- a/dapp/.env
+++ /dev/null
@@ -1,30 +0,0 @@
-VITE_USE_TESTNET=true
-
-# Configuration of sentry.io
-VITE_SENTRY_SUPPORT=false
-# TODO: Sentry DSN will be added during the application building process when it is ready
-VITE_SENTRY_DSN=""
-
-# TODO: Use a more general source
-VITE_ETH_HOSTNAME_HTTP="https://sepolia.infura.io/v3/c80e8ccdcc4c4a809bce4fc165310617"
-VITE_REFERRAL=0
-
-# ENDPOINTS
-VITE_TBTC_API_ENDPOINT=""
-VITE_ACRE_API_ENDPOINT="http://localhost:8788/api/v1/"
-
-# API KEYS
-VITE_GELATO_RELAY_API_KEY="htaJCy_XHj8WsE3w53WBMurfySDtjLP_TrNPPa6IPIc_" # this key should not be used on production
-# Get the API key from: https://thegraph.com/studio/apikeys/.
-VITE_SUBGRAPH_API_KEY=""
-
-# Feature flags
-VITE_FEATURE_FLAG_GAMIFICATION_ENABLED="false"
-VITE_FEATURE_FLAG_WITHDRAWALS_ENABLED="false"
-VITE_FEATURE_FLAG_OKX_WALLET_ENABLED="false"
-VITE_FEATURE_FLAG_XVERSE_WALLET_ENABLED="false"
-VITE_FEATURE_FLAG_ACRE_POINTS_ENABLED="true"
-VITE_FEATURE_FLAG_TVL_ENABLED="true"
-VITE_FEATURE_GATING_DAPP_ENABLED="true"
-VITE_FEATURE_MOBILE_MODE_ENABLED="true"
-
diff --git a/dapp/.env.example b/dapp/.env.example
new file mode 100644
index 000000000..745d7f59b
--- /dev/null
+++ b/dapp/.env.example
@@ -0,0 +1,32 @@
+# Network
+VITE_USE_TESTNET=true
+
+# Basic UI settings
+VITE_REFERRAL=0
+
+# Endpoints
+VITE_ETH_HOSTNAME_HTTP=""
+VITE_ACRE_API_ENDPOINT="http://localhost:8788/api/v1/"
+VITE_TBTC_API_ENDPOINT="http://localhost:8788/tbtc-api/v1/"
+
+# API keys
+VITE_GELATO_RELAY_API_KEY="htaJCy_XHj8WsE3w53WBMurfySDtjLP_TrNPPa6IPIc_" # this key should not be used on production
+VITE_SUBGRAPH_API_KEY=""
+
+# Sentry
+VITE_SENTRY_SUPPORT=false
+VITE_SENTRY_DSN=""
+
+# Posthog
+VITE_POSTHOG_API_HOST="https://us.i.posthog.com"
+VITE_POSTHOG_API_KEY=""
+
+# Feature flags
+VITE_FEATURE_FLAG_WITHDRAWALS_ENABLED="true"
+VITE_FEATURE_FLAG_OKX_WALLET_ENABLED="true"
+VITE_FEATURE_FLAG_XVERSE_WALLET_ENABLED="true"
+VITE_FEATURE_FLAG_ACRE_POINTS_ENABLED="true"
+VITE_FEATURE_FLAG_TVL_ENABLED="true"
+VITE_FEATURE_GATING_DAPP_ENABLED="true"
+VITE_FEATURE_POSTHOG_ENABLED="false"
+VITE_FEATURE_MOBILE_MODE_ENABLED="true"
diff --git a/dapp/README.md b/dapp/README.md
index 349464532..3d3f02761 100644
--- a/dapp/README.md
+++ b/dapp/README.md
@@ -1,10 +1,10 @@
# Acre dApp
-The application is integrate with OrangeKit and allows people to earn yield on their Bitcoin via yield farming on Ethereum.
+The application is integrated with OrangeKit and allows people to earn yield on their Bitcoin via yield farming on Ethereum.
This project was bootstrapped with [Create Vite](https://github.com/vitejs/vite/tree/main/packages/create-vite).
-To access the dApp in Ledger Live import manifest as described in the
+To access the dApp in Ledger Live import the manifest as described in the
[Ledger Live Setup](#ledger-live-setup) section.
### Development
@@ -29,7 +29,7 @@ Install dependencies and start the dApp:
### Environmental variables
-To make sure dApp is running correctly, include the following variables in `.env` file:
+To make sure dApp is running correctly, include the following variables in the `.env` file:
```bash
VITE_TBTC_API_ENDPOINT=
diff --git a/dapp/manifests/ledger-live/ledger-live-manifest-development.json b/dapp/manifests/ledger-live/ledger-live-manifest-development.json
index 84e58fcad..bffaf7026 100644
--- a/dapp/manifests/ledger-live/ledger-live-manifest-development.json
+++ b/dapp/manifests/ledger-live/ledger-live-manifest-development.json
@@ -23,6 +23,7 @@
"account.list",
"bitcoin.getAddress",
"bitcoin.getPublicKey",
+ "bitcoin.getXPub",
"transaction.signAndBroadcast",
"custom.acre.messageSign",
"custom.acre.transactionSignAndBroadcast"
diff --git a/dapp/manifests/ledger-live/ledger-live-manifest-mainnet.json b/dapp/manifests/ledger-live/ledger-live-manifest-mainnet.json
index 281992191..22f74bbc5 100644
--- a/dapp/manifests/ledger-live/ledger-live-manifest-mainnet.json
+++ b/dapp/manifests/ledger-live/ledger-live-manifest-mainnet.json
@@ -23,6 +23,7 @@
"account.list",
"bitcoin.getAddress",
"bitcoin.getPublicKey",
+ "bitcoin.getXPub",
"transaction.signAndBroadcast",
"custom.acre.messageSign",
"custom.acre.transactionSignAndBroadcast"
diff --git a/dapp/manifests/ledger-live/ledger-live-manifest-testnet.json b/dapp/manifests/ledger-live/ledger-live-manifest-testnet.json
index 32dd4a45f..a6ea2019c 100644
--- a/dapp/manifests/ledger-live/ledger-live-manifest-testnet.json
+++ b/dapp/manifests/ledger-live/ledger-live-manifest-testnet.json
@@ -23,6 +23,7 @@
"account.list",
"bitcoin.getAddress",
"bitcoin.getPublicKey",
+ "bitcoin.getXPub",
"transaction.signAndBroadcast",
"custom.acre.messageSign",
"custom.acre.transactionSignAndBroadcast"
diff --git a/dapp/manifests/ledger-live/ledger-manifest-template.json b/dapp/manifests/ledger-live/ledger-manifest-template.json
index e999601d2..980c407d2 100644
--- a/dapp/manifests/ledger-live/ledger-manifest-template.json
+++ b/dapp/manifests/ledger-live/ledger-manifest-template.json
@@ -23,6 +23,7 @@
"account.list",
"bitcoin.getAddress",
"bitcoin.getPublicKey",
+ "bitcoin.getXPub",
"transaction.signAndBroadcast",
"custom.acre.messageSign",
"custom.acre.transactionSignAndBroadcast"
diff --git a/dapp/package.json b/dapp/package.json
index 97601a1a3..b5db82c64 100644
--- a/dapp/package.json
+++ b/dapp/package.json
@@ -23,8 +23,8 @@
"@emotion/styled": "^11.11.0",
"@ledgerhq/wallet-api-acre-module": "0.1.0",
"@ledgerhq/wallet-api-client": "1.6.0",
- "@orangekit/react": "1.0.0-beta.33",
- "@orangekit/sign-in-with-wallet": "1.0.0-beta.6",
+ "@orangekit/react": "1.0.0-beta.34",
+ "@orangekit/sign-in-with-wallet": "1.0.0-beta.7",
"@reduxjs/toolkit": "^2.2.0",
"@rehooks/local-storage": "^2.4.5",
"@safe-global/safe-core-sdk-types": "^5.0.1",
@@ -40,6 +40,7 @@
"framer-motion": "^10.16.5",
"luxon": "^3.5.0",
"mustache": "^4.2.0",
+ "posthog-js": "^1.186.1",
"react": "^18.2.0",
"react-confetti-explosion": "^2.1.2",
"react-dom": "^18.2.0",
diff --git a/dapp/src/DApp.tsx b/dapp/src/DApp.tsx
index 31255b53d..51149f8f7 100644
--- a/dapp/src/DApp.tsx
+++ b/dapp/src/DApp.tsx
@@ -7,11 +7,7 @@ import { QueryClientProvider } from "@tanstack/react-query"
import { ReactQueryDevtools } from "@tanstack/react-query-devtools"
import { AcreSdkProvider } from "./acre-react/contexts"
import GlobalStyles from "./components/GlobalStyles"
-import {
- DocsDrawerContextProvider,
- SidebarContextProvider,
- WalletConnectionErrorContextProvider,
-} from "./contexts"
+import { WalletConnectionAlertContextProvider } from "./contexts"
import { useInitApp } from "./hooks"
import { router } from "./router"
import { store } from "./store"
@@ -19,6 +15,7 @@ import getWagmiConfig from "./wagmiConfig"
import queryClient from "./queryClient"
import { delay, logPromiseFailure } from "./utils"
import { AcreLogo } from "./assets/icons"
+import PostHogProvider from "./posthog/PostHogProvider"
function SplashPage() {
return (
@@ -65,15 +62,13 @@ function DAppProviders() {
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/dapp/src/assets/icons/ArrowUpRight.tsx b/dapp/src/assets/icons/ArrowUpRight.tsx
deleted file mode 100644
index a61f54b38..000000000
--- a/dapp/src/assets/icons/ArrowUpRight.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import React from "react"
-import { createIcon } from "@chakra-ui/react"
-
-export const ArrowUpRight = createIcon({
- displayName: "ArrowUpRight",
- viewBox: "0 0 16 17",
- path: (
-
- ),
-})
diff --git a/dapp/src/assets/icons/BoltFilled.tsx b/dapp/src/assets/icons/BoltFilled.tsx
deleted file mode 100644
index 8252422d9..000000000
--- a/dapp/src/assets/icons/BoltFilled.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import React from "react"
-import { createIcon } from "@chakra-ui/react"
-
-export default createIcon({
- displayName: "BoltFilled",
- viewBox: "0 0 24 24",
- path: [
- ,
- ],
-})
diff --git a/dapp/src/assets/icons/index.ts b/dapp/src/assets/icons/index.ts
index 7a8fe57c2..eb50fc819 100644
--- a/dapp/src/assets/icons/index.ts
+++ b/dapp/src/assets/icons/index.ts
@@ -1,4 +1,3 @@
-export * from "./ArrowUpRight"
export * from "./AcreLogo"
export * from "./Pause"
export { default as LoadingSpinnerSuccessIcon } from "./LoadingSpinnerSuccessIcon"
@@ -8,4 +7,3 @@ export * from "./MezoSignIcon"
export * from "./AcreSignIcon"
export * from "./BitcoinsStackErrorIcon"
export { default as MatsIcon } from "./MatsIcon"
-export { default as BoltFilled } from "./BoltFilled"
diff --git a/dapp/src/assets/images/benefits/bibos-beehive.svg b/dapp/src/assets/images/benefits/bibos-beehive.svg
deleted file mode 100644
index 784888e3c..000000000
--- a/dapp/src/assets/images/benefits/bibos-beehive.svg
+++ /dev/null
@@ -1,60 +0,0 @@
-
diff --git a/dapp/src/assets/images/benefits/index.ts b/dapp/src/assets/images/benefits/index.ts
deleted file mode 100644
index 072363998..000000000
--- a/dapp/src/assets/images/benefits/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export { default as rewardsBoostImage } from "#/assets/images/benefits/rewards-boost.svg"
-export { default as seasonKeyImage } from "#/assets/images/benefits/season-key.svg"
-export { default as rewardsBoostArrowImage } from "#/assets/images/benefits/rewards-boost-arrow.svg"
-export { default as bibosBeehiveImage } from "#/assets/images/benefits/bibos-beehive.svg"
diff --git a/dapp/src/assets/images/benefits/rewards-boost-arrow.svg b/dapp/src/assets/images/benefits/rewards-boost-arrow.svg
deleted file mode 100644
index b8cb985cf..000000000
--- a/dapp/src/assets/images/benefits/rewards-boost-arrow.svg
+++ /dev/null
@@ -1,68 +0,0 @@
-
diff --git a/dapp/src/assets/images/benefits/rewards-boost.svg b/dapp/src/assets/images/benefits/rewards-boost.svg
deleted file mode 100644
index 70d7f5144..000000000
--- a/dapp/src/assets/images/benefits/rewards-boost.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/dapp/src/assets/images/benefits/season-key.svg b/dapp/src/assets/images/benefits/season-key.svg
deleted file mode 100644
index e40917ff3..000000000
--- a/dapp/src/assets/images/benefits/season-key.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/dapp/src/assets/images/mezo-beehive-modal-illustration.svg b/dapp/src/assets/images/mezo-beehive-modal-illustration.svg
deleted file mode 100644
index 18ee51bba..000000000
--- a/dapp/src/assets/images/mezo-beehive-modal-illustration.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/dapp/src/assets/images/season-section-background.png b/dapp/src/assets/images/season-section-background.png
deleted file mode 100644
index a6f2dfbdc..000000000
Binary files a/dapp/src/assets/images/season-section-background.png and /dev/null differ
diff --git a/dapp/src/assets/images/season-section-foreground.png b/dapp/src/assets/images/season-section-foreground.png
deleted file mode 100644
index e8554ba51..000000000
Binary files a/dapp/src/assets/images/season-section-foreground.png and /dev/null differ
diff --git a/dapp/src/assets/webps/confetti.webp b/dapp/src/assets/webps/confetti.webp
deleted file mode 100644
index f99786801..000000000
Binary files a/dapp/src/assets/webps/confetti.webp and /dev/null differ
diff --git a/dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx b/dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx
new file mode 100644
index 000000000..44a499d87
--- /dev/null
+++ b/dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx
@@ -0,0 +1,118 @@
+import React from "react"
+import { AlertStatus, Box, Link, VStack } from "@chakra-ui/react"
+import { AnimatePresence, Variants, motion } from "framer-motion"
+import { EXTERNAL_HREF } from "#/constants"
+import {
+ Alert,
+ AlertDescription,
+ AlertTitle,
+ AlertIcon,
+ AlertProps,
+} from "../shared/Alert"
+
+export enum ConnectionAlert {
+ Rejected = "REJECTED",
+ NotSupported = "NOT_SUPPORTED",
+ NetworkMismatch = "NETWORK_MISMATCH",
+ InvalidSIWWSignature = "INVALID_SIWW_SIGNATURE",
+ Default = "DEFAULT",
+}
+
+type ConnectionAlertData = {
+ title: string
+ description?: React.ReactNode
+ status?: AlertStatus
+ colorScheme?: string
+}
+
+type ConnectionAlerts = Record
+
+function ContactSupport() {
+ return (
+
+ If the problem persists, contact{" "}
+
+ Acre support
+
+ .
+
+ )
+}
+
+const CONNECTION_ALERTS: ConnectionAlerts = {
+ [ConnectionAlert.Rejected]: {
+ title: "Please connect your wallet to start using Acre",
+ status: "info",
+ colorScheme: "blue",
+ },
+ [ConnectionAlert.NotSupported]: {
+ title: "Not supported.",
+ description:
+ "Only Native SegWit, Nested SegWit, or Legacy addresses are supported. Please use a compatible address or switch to a different wallet.",
+ },
+ [ConnectionAlert.NetworkMismatch]: {
+ title: "Incorrect network detected in your wallet.",
+ description:
+ "Please connect your wallet to the correct Bitcoin network and try again.",
+ },
+ [ConnectionAlert.Default]: {
+ title: "Wallet connection failed. Please try again.",
+ description: ,
+ },
+ [ConnectionAlert.InvalidSIWWSignature]: {
+ title: "Invalid sign-in signature. Please try again.",
+ description: ,
+ },
+}
+
+const collapseVariants: Variants = {
+ collapsed: { height: 0 },
+ expanded: { height: "auto" },
+}
+
+type ConnectWalletAlertProps = Omit & {
+ type?: ConnectionAlert
+}
+
+export default function ConnectWalletAlert(props: ConnectWalletAlertProps) {
+ const { type, ...restProps } = props
+
+ const {
+ status = "error",
+ title,
+ description,
+ ...restData
+ } = (type ? CONNECTION_ALERTS[type] : {}) as ConnectionAlertData
+
+ return (
+
+ {type && (
+
+
+
+
+ {title}
+ {description && (
+ {description}
+ )}
+
+
+
+ )}
+
+ )
+}
diff --git a/dapp/src/components/ConnectWalletModal/ConnectWalletButton.tsx b/dapp/src/components/ConnectWalletModal/ConnectWalletButton.tsx
index 0747fdc8e..b950b2b2c 100644
--- a/dapp/src/components/ConnectWalletModal/ConnectWalletButton.tsx
+++ b/dapp/src/components/ConnectWalletModal/ConnectWalletButton.tsx
@@ -1,12 +1,12 @@
import React, { useCallback, useEffect, useRef, useState } from "react"
-import { CONNECTION_ERRORS, ONE_SEC_IN_MILLISECONDS } from "#/constants"
+import { ONE_SEC_IN_MILLISECONDS } from "#/constants"
import {
useAppDispatch,
useIsEmbed,
useModal,
useSignMessageAndCreateSession,
useWallet,
- useWalletConnectionError,
+ useWalletConnectionAlert,
} from "#/hooks"
import { setIsSignedMessage } from "#/store/wallet"
import { OrangeKitConnector, OrangeKitError, OnSuccessCallback } from "#/types"
@@ -24,10 +24,12 @@ import {
} from "@chakra-ui/react"
import { IconArrowNarrowRight } from "@tabler/icons-react"
import { AnimatePresence, Variants, motion } from "framer-motion"
+import { usePostHogIdentity } from "#/hooks/posthog"
import ArrivingSoonTooltip from "../ArrivingSoonTooltip"
import { TextLg, TextMd } from "../shared/Typography"
import ConnectWalletStatusLabel from "./ConnectWalletStatusLabel"
import Spinner from "../shared/Spinner"
+import { ConnectionAlert } from "./ConnectWalletAlert"
type ConnectWalletButtonProps = {
label: string
@@ -66,15 +68,16 @@ export default function ConnectWalletButton({
} = useWallet()
const { signMessageStatus, resetMessageStatus, signMessageAndCreateSession } =
useSignMessageAndCreateSession()
- const { connectionError, setConnectionError, resetConnectionError } =
- useWalletConnectionError()
+ const { type, setConnectionAlert, resetConnectionAlert } =
+ useWalletConnectionAlert()
const { closeModal } = useModal()
const dispatch = useAppDispatch()
const isMounted = useRef(false)
+ const { handleIdentification } = usePostHogIdentity()
const [isLoading, setIsLoading] = useState(false)
- const hasConnectionError = connectionError || connectionStatus === "error"
+ const hasConnectionError = type || connectionStatus === "error"
const hasSignMessageErrorStatus = signMessageStatus === "error"
const shouldShowStatuses = isSelected && !hasConnectionError
const shouldShowRetryButton = address && hasSignMessageErrorStatus
@@ -99,14 +102,14 @@ export default function ConnectWalletButton({
onDisconnect()
console.error("Failed to sign siww message", error)
- setConnectionError(CONNECTION_ERRORS.INVALID_SIWW_SIGNATURE)
+ setConnectionAlert(ConnectionAlert.InvalidSIWWSignature)
}
},
[
signMessageAndCreateSession,
onSuccessSignMessage,
onDisconnect,
- setConnectionError,
+ setConnectionAlert,
],
)
@@ -117,8 +120,11 @@ export default function ConnectWalletButton({
if (!btcAddress) return
await handleSignMessageAndCreateSession(connector, btcAddress)
+ handleIdentification(btcAddress, {
+ connector: connectedConnector.id,
+ })
},
- [connector, handleSignMessageAndCreateSession],
+ [connector, handleSignMessageAndCreateSession, handleIdentification],
)
const handleConnection = useCallback(() => {
@@ -129,14 +135,19 @@ export default function ConnectWalletButton({
},
onError: (error: OrangeKitError) => {
const errorData = orangeKit.parseOrangeKitConnectionError(error)
- setConnectionError(errorData)
+
+ if (errorData === ConnectionAlert.Default) {
+ console.error("Failed to connect", error)
+ }
+
+ setConnectionAlert(errorData)
},
})
}, [
onConnect,
connector,
onSuccessConnection,
- setConnectionError,
+ setConnectionAlert,
isReconnecting,
])
@@ -159,7 +170,7 @@ export default function ConnectWalletButton({
if (shouldShowStatuses) return
if (!isReconnecting) onDisconnect()
- resetConnectionError()
+ resetConnectionAlert()
resetMessageStatus()
const isInstalled = orangeKit.isWalletInstalled(connector)
diff --git a/dapp/src/components/ConnectWalletModal/ConnectWalletErrorAlert.tsx b/dapp/src/components/ConnectWalletModal/ConnectWalletErrorAlert.tsx
deleted file mode 100644
index 2e97b59ea..000000000
--- a/dapp/src/components/ConnectWalletModal/ConnectWalletErrorAlert.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import React from "react"
-import { Box, VStack } from "@chakra-ui/react"
-import { AnimatePresence, Variants, motion } from "framer-motion"
-import { ConnectionErrorData } from "#/types"
-import {
- Alert,
- AlertDescription,
- AlertTitle,
- AlertIcon,
- AlertProps,
-} from "../shared/Alert"
-
-type ConnectWalletErrorAlertProps = AlertProps & Partial
-
-const collapseVariants: Variants = {
- collapsed: { height: 0 },
- expanded: { height: "auto" },
-}
-
-export default function ConnectWalletErrorAlert(
- props: ConnectWalletErrorAlertProps,
-) {
- const { title, description, ...restProps } = props
-
- const shouldRender = !!(title && description)
-
- return (
-
- {shouldRender && (
-
-
-
-
- {title}
- {description}
-
-
-
- )}
-
- )
-}
diff --git a/dapp/src/components/ConnectWalletModal/index.tsx b/dapp/src/components/ConnectWalletModal/index.tsx
index 66816f2be..23404c9b9 100644
--- a/dapp/src/components/ConnectWalletModal/index.tsx
+++ b/dapp/src/components/ConnectWalletModal/index.tsx
@@ -5,13 +5,13 @@ import {
useIsEmbed,
useIsSignedMessage,
useWallet,
- useWalletConnectionError,
+ useWalletConnectionAlert,
} from "#/hooks"
import { OrangeKitConnector, BaseModalProps, OnSuccessCallback } from "#/types"
import { wallets } from "#/constants"
import withBaseModal from "../ModalRoot/withBaseModal"
import ConnectWalletButton from "./ConnectWalletButton"
-import ConnectWalletErrorAlert from "./ConnectWalletErrorAlert"
+import ConnectWalletAlert from "./ConnectWalletAlert"
export function ConnectWalletModalBase({
onSuccess,
@@ -30,7 +30,7 @@ export function ConnectWalletModalBase({
}))
const [selectedConnectorId, setSelectedConnectorId] = useState()
- const { connectionError, resetConnectionError } = useWalletConnectionError()
+ const { type, resetConnectionAlert } = useWalletConnectionAlert()
const isSignedMessage = useIsSignedMessage()
const handleButtonOnClick = (connector: OrangeKitConnector) => {
@@ -48,7 +48,7 @@ export function ConnectWalletModalBase({
{withCloseButton && (
{
- resetConnectionError()
+ resetConnectionAlert()
if (!isSignedMessage) {
onDisconnect()
@@ -59,7 +59,7 @@ export function ConnectWalletModalBase({
{`Select your ${isEmbed ? "account" : "wallet"}`}
-
+
{enabledConnectors.map((connector) => (
-
-
-
- {/* TODO: Add a documentation */}
- Documentation
-
-
-
- )
-}
diff --git a/dapp/src/components/Footer.tsx b/dapp/src/components/Footer.tsx
index d80161d7f..c32c4c7ed 100644
--- a/dapp/src/components/Footer.tsx
+++ b/dapp/src/components/Footer.tsx
@@ -10,8 +10,9 @@ import {
Icon,
} from "@chakra-ui/react"
import { EXTERNAL_HREF } from "#/constants"
-import { AcreSignIcon, ArrowUpRight } from "#/assets/icons"
+import { AcreSignIcon } from "#/assets/icons"
import { useMobileMode } from "#/hooks"
+import { IconArrowUpRight } from "@tabler/icons-react"
type FooterListItem = Pick
@@ -64,7 +65,7 @@ const getItemsList = (
as={Link}
__css={styles.link}
iconSpacing={0}
- rightIcon={}
+ rightIcon={}
{...link}
isExternal
/>
diff --git a/dapp/src/components/GlobalStyles/index.tsx b/dapp/src/components/GlobalStyles.tsx
similarity index 100%
rename from dapp/src/components/GlobalStyles/index.tsx
rename to dapp/src/components/GlobalStyles.tsx
diff --git a/dapp/src/components/Header/ConnectWallet.tsx b/dapp/src/components/Header/ConnectWallet.tsx
index b1b598746..8040b47da 100644
--- a/dapp/src/components/Header/ConnectWallet.tsx
+++ b/dapp/src/components/Header/ConnectWallet.tsx
@@ -5,11 +5,15 @@ import {
HStack,
Icon,
IconButton,
+ Menu,
+ MenuButton,
+ MenuItem,
+ MenuList,
StackDivider,
useClipboard,
useMultiStyleConfig,
} from "@chakra-ui/react"
-import { useIsEmbed, useModal, useWallet } from "#/hooks"
+import { useIsEmbed, useMobileMode, useModal, useWallet } from "#/hooks"
import { CurrencyBalance } from "#/components/shared/CurrencyBalance"
import { TextMd } from "#/components/shared/Typography"
import { BitcoinIcon } from "#/assets/icons"
@@ -21,8 +25,11 @@ import {
IconLogout,
IconWallet,
IconUserCode,
+ IconChevronDown,
+ IconChevronUp,
} from "@tabler/icons-react"
import { useMatch } from "react-router-dom"
+import { usePostHogIdentity } from "#/hooks/posthog"
import Tooltip from "../shared/Tooltip"
function isChangeAccountFeatureSupported(embeddedApp: string | undefined) {
@@ -39,11 +46,18 @@ export default function ConnectWallet() {
size: "lg",
})
const isDashboardPage = useMatch("/dashboard")
+ const { resetIdentity } = usePostHogIdentity()
+ const isMobile = useMobileMode()
const handleConnectWallet = (isReconnecting: boolean = false) => {
openModal(MODAL_TYPES.CONNECT_WALLET, { isReconnecting })
}
+ const handleDisconnectWallet = () => {
+ onDisconnect()
+ resetIdentity()
+ }
+
if (!address) {
return (
- )
-}
diff --git a/dapp/src/components/shared/CurrencyBalance/index.tsx b/dapp/src/components/shared/CurrencyBalance.tsx
similarity index 98%
rename from dapp/src/components/shared/CurrencyBalance/index.tsx
rename to dapp/src/components/shared/CurrencyBalance.tsx
index bf7f65750..1b5aa3be7 100644
--- a/dapp/src/components/shared/CurrencyBalance/index.tsx
+++ b/dapp/src/components/shared/CurrencyBalance.tsx
@@ -12,7 +12,7 @@ import {
numberToLocaleString,
} from "#/utils"
import { CurrencyType, AmountType } from "#/types"
-import Tooltip from "../Tooltip"
+import Tooltip from "./Tooltip"
export type CurrencyBalanceProps = {
currency: CurrencyType
diff --git a/dapp/src/components/shared/CurrencyBalanceWithConversion/index.tsx b/dapp/src/components/shared/CurrencyBalanceWithConversion.tsx
similarity index 85%
rename from dapp/src/components/shared/CurrencyBalanceWithConversion/index.tsx
rename to dapp/src/components/shared/CurrencyBalanceWithConversion.tsx
index 5d19dd126..ea9f64850 100644
--- a/dapp/src/components/shared/CurrencyBalanceWithConversion/index.tsx
+++ b/dapp/src/components/shared/CurrencyBalanceWithConversion.tsx
@@ -1,6 +1,6 @@
import React from "react"
import { useCurrencyConversion } from "#/hooks"
-import { CurrencyBalance, CurrencyBalanceProps } from "../CurrencyBalance"
+import { CurrencyBalance, CurrencyBalanceProps } from "./CurrencyBalance"
export function CurrencyBalanceWithConversion({
from,
diff --git a/dapp/src/components/shared/FeesDetails/FeesItem.tsx b/dapp/src/components/shared/FeesDetails/FeesDetailsAmountItem.tsx
similarity index 100%
rename from dapp/src/components/shared/FeesDetails/FeesItem.tsx
rename to dapp/src/components/shared/FeesDetails/FeesDetailsAmountItem.tsx
diff --git a/dapp/src/components/shared/IconTag.tsx b/dapp/src/components/shared/IconTag.tsx
deleted file mode 100644
index 7c2f8b51e..000000000
--- a/dapp/src/components/shared/IconTag.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import React from "react"
-import { Tag, TagLeftIcon, TagLabel, TagProps, Icon } from "@chakra-ui/react"
-
-type IconTagProps = TagProps & {
- icon: typeof Icon
-}
-
-export default function IconTag(props: IconTagProps) {
- const { children, icon, ...restProps } = props
-
- return (
-
-
- {children}
-
- )
-}
diff --git a/dapp/src/components/shared/NavLink.tsx b/dapp/src/components/shared/NavLink.tsx
deleted file mode 100644
index 305bbfe53..000000000
--- a/dapp/src/components/shared/NavLink.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import React from "react"
-import {
- Link as ChakraLink,
- LinkProps as ChakraLinkProps,
-} from "@chakra-ui/react"
-import {
- NavLink as RouterNavLink,
- NavLinkProps as RouterNavLinkProps,
-} from "react-router-dom"
-
-export type NavLinkProps = Omit &
- Pick
-
-export function NavLink(props: NavLinkProps) {
- const { children, ...restProps } = props
- return (
-
- {children as React.ReactNode}
-
- )
-}
diff --git a/dapp/src/components/shared/NumberFormatInput/index.tsx b/dapp/src/components/shared/NumberFormatInput.tsx
similarity index 100%
rename from dapp/src/components/shared/NumberFormatInput/index.tsx
rename to dapp/src/components/shared/NumberFormatInput.tsx
diff --git a/dapp/src/components/shared/ProgressBar.tsx b/dapp/src/components/shared/ProgressBar.tsx
index e5467bc2d..ca8b52c1d 100644
--- a/dapp/src/components/shared/ProgressBar.tsx
+++ b/dapp/src/components/shared/ProgressBar.tsx
@@ -1,6 +1,6 @@
import React from "react"
import { Progress, ProgressProps, ProgressLabel, Icon } from "@chakra-ui/react"
-import { BoltFilled } from "#/assets/icons"
+import { IconBolt } from "@tabler/icons-react"
type ProgressBarProps = ProgressProps & {
withBoltIcon?: boolean
@@ -23,7 +23,8 @@ function ProgressBar(props: ProgressBarProps) {
transform="auto"
translateX="-100%"
translateY="-50%"
- as={BoltFilled}
+ as={IconBolt}
+ fill="currentcolor"
mx={-1}
/>
)}
diff --git a/dapp/src/components/shared/SeasonSectionBackground.tsx b/dapp/src/components/shared/SeasonSectionBackground.tsx
deleted file mode 100644
index 027775059..000000000
--- a/dapp/src/components/shared/SeasonSectionBackground.tsx
+++ /dev/null
@@ -1,125 +0,0 @@
-import React, { useRef } from "react"
-import { Box, BoxProps } from "@chakra-ui/react"
-import { useSize } from "@chakra-ui/react-use-size"
-import {
- MotionValue,
- motion,
- useScroll,
- useSpring,
- useTransform,
- useTime,
- wrap,
-} from "framer-motion"
-import seasonBackground from "#/assets/images/season-section-background.png"
-import seasonForeground from "#/assets/images/season-section-foreground.png"
-
-export function SeasonSectionBackground(props: BoxProps) {
- const containerRef = useRef(null)
- const { scrollYProgress } = useScroll({
- target: containerRef,
- offset: ["center start", "start end"],
- })
- const smoothScrollYProgress = useSpring(scrollYProgress, {
- damping: 10,
- stiffness: 90,
- mass: 0.75,
- }) as MotionValue
- const foregroundParallax = useTransform(
- smoothScrollYProgress,
- [0, 1],
- ["45%", "65%"],
- )
- const time = useTime()
- // Seed value is wrapped to prevent infinite increment causing potential memory leaks
- const seed = useTransform(time, (value) => wrap(0, 2137, Math.floor(value)))
-
- const size = useSize(containerRef)
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )
-}
diff --git a/dapp/src/components/shared/Skeleton/index.tsx b/dapp/src/components/shared/Skeleton.tsx
similarity index 100%
rename from dapp/src/components/shared/Skeleton/index.tsx
rename to dapp/src/components/shared/Skeleton.tsx
diff --git a/dapp/src/components/shared/Spinner/index.tsx b/dapp/src/components/shared/Spinner.tsx
similarity index 100%
rename from dapp/src/components/shared/Spinner/index.tsx
rename to dapp/src/components/shared/Spinner.tsx
diff --git a/dapp/src/components/shared/TokenBalanceInput/index.tsx b/dapp/src/components/shared/TokenBalanceInput.tsx
similarity index 96%
rename from dapp/src/components/shared/TokenBalanceInput/index.tsx
rename to dapp/src/components/shared/TokenBalanceInput.tsx
index cd92ff160..69067f35d 100644
--- a/dapp/src/components/shared/TokenBalanceInput/index.tsx
+++ b/dapp/src/components/shared/TokenBalanceInput.tsx
@@ -21,9 +21,9 @@ import { useCurrencyConversion } from "#/hooks"
import NumberFormatInput, {
NumberFormatInputValues,
NumberFormatInputProps,
-} from "../NumberFormatInput"
-import { CurrencyBalance } from "../CurrencyBalance"
-import HelperErrorText, { HelperErrorTextProps } from "../Form/HelperErrorText"
+} from "./NumberFormatInput"
+import { CurrencyBalance } from "./CurrencyBalance"
+import HelperErrorText, { HelperErrorTextProps } from "./Form/HelperErrorText"
type FiatCurrencyBalanceProps = {
amount: bigint
diff --git a/dapp/src/components/shared/Tooltip.tsx b/dapp/src/components/shared/Tooltip.tsx
index 924e0e950..6c6944593 100644
--- a/dapp/src/components/shared/Tooltip.tsx
+++ b/dapp/src/components/shared/Tooltip.tsx
@@ -1,5 +1,5 @@
import React, { useState } from "react"
-import { Box, Tooltip as ChakraTooltip, TooltipProps } from "@chakra-ui/react"
+import { Tooltip as ChakraTooltip, TooltipProps, Flex } from "@chakra-ui/react"
export default function Tooltip(props: TooltipProps) {
const { children, ...restProps } = props
@@ -7,13 +7,13 @@ export default function Tooltip(props: TooltipProps) {
return (
- setIsOpen(true)}
onMouseLeave={() => setIsOpen(false)}
onClick={() => setIsOpen(true)}
>
{children}
-
+
)
}
diff --git a/dapp/src/components/shared/InfoTooltip.tsx b/dapp/src/components/shared/TooltipIcon.tsx
similarity index 50%
rename from dapp/src/components/shared/InfoTooltip.tsx
rename to dapp/src/components/shared/TooltipIcon.tsx
index a9394b460..1ef97745f 100644
--- a/dapp/src/components/shared/InfoTooltip.tsx
+++ b/dapp/src/components/shared/TooltipIcon.tsx
@@ -1,16 +1,21 @@
import React from "react"
-import { IconInfoCircleFilled } from "@tabler/icons-react"
+import { IconInfoCircleFilled, TablerIcon } from "@tabler/icons-react"
import { Icon, TooltipProps } from "@chakra-ui/react"
import Tooltip from "./Tooltip"
// TODO: Define in the new color palette
const ICON_COLOR = "#3A3328"
-export default function InfoTooltip(props: Omit) {
+type TooltipIconProps = Omit & {
+ icon?: TablerIcon
+}
+
+export default function TooltipIcon(props: TooltipIconProps) {
+ const { icon, ...restProps } = props
return (
-
+
= {
- REJECTED: {
- title: "Wallet connection rejected.",
- description: "If you encountered an error, please try again.",
- },
- NOT_SUPPORTED: {
- title: "Not supported.",
- description:
- "Only Native SegWit, Nested SegWit or Legacy addresses supported at this time. Please try a different address or another wallet.",
- },
- NETWORK_MISMATCH: {
- title: "Error!",
- description:
- "Incorrect network detected in your wallet. Please choose proper network and try again.",
- },
- DEFAULT: {
- title: "Something went wrong...",
- description: "We encountered an error. Please try again.",
- },
- INVALID_SIWW_SIGNATURE: {
- title: "Invalid Sign In With Wallet signature",
- description: "We encountered an error. Please try again.",
- },
-}
+import { ACTION_FLOW_TYPES } from "#/types"
export const TOKEN_FORM_ERRORS = {
REQUIRED: "Please enter an amount.",
diff --git a/dapp/src/constants/featureFlags.ts b/dapp/src/constants/featureFlags.ts
index ddc1ef2ee..4a70ec623 100644
--- a/dapp/src/constants/featureFlags.ts
+++ b/dapp/src/constants/featureFlags.ts
@@ -1,6 +1,3 @@
-const GAMIFICATION_ENABLED =
- import.meta.env.VITE_FEATURE_FLAG_GAMIFICATION_ENABLED === "true"
-
const OKX_WALLET_ENABLED =
import.meta.env.VITE_FEATURE_FLAG_OKX_WALLET_ENABLED === "true"
@@ -18,17 +15,19 @@ const TVL_ENABLED = import.meta.env.VITE_FEATURE_FLAG_TVL_ENABLED === "true"
const GATING_DAPP_ENABLED =
import.meta.env.VITE_FEATURE_GATING_DAPP_ENABLED === "true"
+const POSTHOG_ENABLED = import.meta.env.VITE_FEATURE_POSTHOG_ENABLED === "true"
+
const MOBILE_MODE_ENABLED =
import.meta.env.VITE_FEATURE_MOBILE_MODE_ENABLED === "true"
const featureFlags = {
- GAMIFICATION_ENABLED,
OKX_WALLET_ENABLED,
XVERSE_WALLET_ENABLED,
WITHDRAWALS_ENABLED,
ACRE_POINTS_ENABLED,
TVL_ENABLED,
GATING_DAPP_ENABLED,
+ POSTHOG_ENABLED,
MOBILE_MODE_ENABLED,
}
diff --git a/dapp/src/constants/index.ts b/dapp/src/constants/index.ts
index 122333c6f..a5e49268c 100644
--- a/dapp/src/constants/index.ts
+++ b/dapp/src/constants/index.ts
@@ -1,4 +1,3 @@
-export * from "./benefits"
export * from "./chains"
export * from "./currency"
export { default as env } from "./env"
@@ -7,7 +6,6 @@ export * from "./externalHref"
export { default as featureFlags } from "./featureFlags"
export { default as queryKeysFactory } from "./queryKeysFactory"
export { default as screen } from "./screen"
-export * from "./staking"
export { default as tbtc } from "./tbtc"
export * from "./time"
export { default as wallets } from "./wallets"
diff --git a/dapp/src/constants/queryKeysFactory.ts b/dapp/src/constants/queryKeysFactory.ts
index f7a543e0c..95a82ff5c 100644
--- a/dapp/src/constants/queryKeysFactory.ts
+++ b/dapp/src/constants/queryKeysFactory.ts
@@ -2,6 +2,7 @@ const userKeys = {
all: ["user"] as const,
balance: () => [...userKeys.all, "balance"] as const,
position: () => [...userKeys.all, "position"] as const,
+ activities: () => [...userKeys.all, "activities"] as const,
pointsData: () => [...userKeys.all, "points-data"] as const,
}
diff --git a/dapp/src/constants/staking.ts b/dapp/src/constants/staking.ts
deleted file mode 100644
index 8b4bd6086..000000000
--- a/dapp/src/constants/staking.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-// TODO: Read the value from the SDK, once we expose it
-export const MINIMUM_BALANCE = BigInt(String(5e6)) // 0.05 BTC
diff --git a/dapp/src/contexts/DocsDrawerContext.tsx b/dapp/src/contexts/DocsDrawerContext.tsx
deleted file mode 100644
index f5097fc96..000000000
--- a/dapp/src/contexts/DocsDrawerContext.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import React, { createContext, useCallback, useMemo, useState } from "react"
-
-type DocsDrawerContextValue = {
- isOpen: boolean
- onOpen: () => void
- onClose: () => void
-}
-
-export const DocsDrawerContext = createContext({
- isOpen: false,
- onOpen: () => {},
- onClose: () => {},
-})
-
-export function DocsDrawerContextProvider({
- children,
-}: {
- children: React.ReactNode
-}): React.ReactElement {
- const [isOpen, setIsOpen] = useState(false)
-
- const onOpen = useCallback(() => {
- setIsOpen(true)
- }, [])
-
- const onClose = useCallback(() => {
- setIsOpen(false)
- }, [])
-
- const contextValue: DocsDrawerContextValue = useMemo(
- () => ({
- isOpen,
- onOpen,
- onClose,
- }),
- [isOpen, onClose, onOpen],
- )
-
- return (
-
- {children}
-
- )
-}
diff --git a/dapp/src/contexts/SidebarContext.tsx b/dapp/src/contexts/SidebarContext.tsx
deleted file mode 100644
index 125b871a1..000000000
--- a/dapp/src/contexts/SidebarContext.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import useIsEmbed from "#/hooks/useIsEmbed"
-import React, { createContext, useCallback, useMemo, useState } from "react"
-
-type SidebarContextValue = {
- isOpen: boolean
- onOpen: () => void
- onClose: () => void
-}
-
-export const SidebarContext = createContext({
- isOpen: false,
- onOpen: () => {},
- onClose: () => {},
-})
-
-export function SidebarContextProvider({
- children,
-}: {
- children: React.ReactNode
-}): React.ReactElement {
- const { isEmbed } = useIsEmbed()
-
- const [isOpen, setIsOpen] = useState(false)
-
- const onOpen = useCallback(() => {
- if (isEmbed) return
-
- setIsOpen(true)
- }, [isEmbed])
-
- const onClose = useCallback(() => {
- setIsOpen(false)
- }, [])
-
- const contextValue: SidebarContextValue = useMemo(
- () => ({
- isOpen,
- onOpen,
- onClose,
- }),
- [isOpen, onClose, onOpen],
- )
-
- return (
-
- {children}
-
- )
-}
diff --git a/dapp/src/contexts/WalletConnectionAlertContext.tsx b/dapp/src/contexts/WalletConnectionAlertContext.tsx
new file mode 100644
index 000000000..ee9faab0c
--- /dev/null
+++ b/dapp/src/contexts/WalletConnectionAlertContext.tsx
@@ -0,0 +1,48 @@
+import { ConnectionAlert } from "#/components/ConnectWalletModal/ConnectWalletAlert"
+import React, { createContext, useCallback, useMemo, useState } from "react"
+
+type WalletConnectionAlertContextValue = {
+ type?: ConnectionAlert
+ setConnectionAlert: (type: ConnectionAlert) => void
+ resetConnectionAlert: () => void
+}
+
+export const WalletConnectionAlertContext =
+ createContext(
+ {} as WalletConnectionAlertContextValue,
+ )
+
+export function WalletConnectionAlertContextProvider({
+ children,
+}: {
+ children: React.ReactNode
+}): React.ReactElement {
+ const [type, setType] = useState()
+
+ const resetConnectionAlert = useCallback(() => {
+ setType(undefined)
+ }, [setType])
+
+ const setConnectionAlert = useCallback(
+ (connectionAlert: ConnectionAlert) => {
+ setType(connectionAlert)
+ },
+ [setType],
+ )
+
+ const contextValue: WalletConnectionAlertContextValue =
+ useMemo(
+ () => ({
+ type,
+ setConnectionAlert,
+ resetConnectionAlert,
+ }),
+ [resetConnectionAlert, setConnectionAlert, type],
+ )
+
+ return (
+
+ {children}
+
+ )
+}
diff --git a/dapp/src/contexts/WalletConnectionErrorContext.tsx b/dapp/src/contexts/WalletConnectionErrorContext.tsx
deleted file mode 100644
index 23838bac2..000000000
--- a/dapp/src/contexts/WalletConnectionErrorContext.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import { ConnectionErrorData } from "#/types"
-import React, { createContext, useCallback, useMemo, useState } from "react"
-
-type WalletConnectionErrorContextValue = {
- connectionError: ConnectionErrorData | undefined
- setConnectionError: (data: ConnectionErrorData) => void
- resetConnectionError: () => void
-}
-
-export const WalletConnectionErrorContext =
- createContext(
- {} as WalletConnectionErrorContextValue,
- )
-
-export function WalletConnectionErrorContextProvider({
- children,
-}: {
- children: React.ReactNode
-}): React.ReactElement {
- const [connectionError, setConnectionError] = useState()
-
- const resetConnectionError = useCallback(
- () => setConnectionError(undefined),
- [setConnectionError],
- )
-
- const contextValue: WalletConnectionErrorContextValue =
- useMemo(
- () => ({
- connectionError,
- setConnectionError,
- resetConnectionError,
- }),
- [connectionError, resetConnectionError, setConnectionError],
- )
-
- return (
-
- {children}
-
- )
-}
diff --git a/dapp/src/contexts/index.tsx b/dapp/src/contexts/index.tsx
index 5781528dd..e3f7fa1aa 100644
--- a/dapp/src/contexts/index.tsx
+++ b/dapp/src/contexts/index.tsx
@@ -1,5 +1,3 @@
-export * from "./DocsDrawerContext"
-export * from "./SidebarContext"
export * from "./StakeFlowContext"
export * from "./PaginationContext"
-export * from "./WalletConnectionErrorContext"
+export * from "./WalletConnectionAlertContext"
diff --git a/dapp/src/hooks/index.ts b/dapp/src/hooks/index.ts
index 9584fa5eb..df7ca251c 100644
--- a/dapp/src/hooks/index.ts
+++ b/dapp/src/hooks/index.ts
@@ -2,8 +2,6 @@ export * from "./store"
export * from "./sdk"
export * from "./orangeKit"
export * from "./useDetectThemeMode"
-export * from "./useSidebar"
-export * from "./useDocsDrawer"
export * from "./useTransactionDetails"
export * from "./useStakeFlowContext"
export * from "./useInitApp"
@@ -20,22 +18,26 @@ export * from "./useTransactionModal"
export * from "./useVerifyDepositAddress"
export { default as useStatistics } from "./useStatistics"
export * from "./useDisconnectWallet"
-export * from "./useWalletConnectionError"
-export { default as useInvalidateQueries } from "./useInvalidateQueries"
+export { default as useWalletConnectionAlert } from "./useWalletConnectionAlert"
export { default as useResetWalletState } from "./useResetWalletState"
export { default as useMobileMode } from "./useMobileMode"
export { default as useBitcoinRecoveryAddress } from "./useBitcoinRecoveryAddress"
export { default as useIsFetchedWalletData } from "./useIsFetchedWalletData"
export { default as useLocalStorage } from "./useLocalStorage"
-export { default as useDetectReferral } from "./useDetectReferral"
export { default as useReferral } from "./useReferral"
-export { default as useMats } from "./useMats"
export { default as useIsEmbed } from "./useIsEmbed"
export { default as useTriggerConnectWalletModal } from "./useTriggerConnectWalletModal"
export { default as useLastUsedBtcAddress } from "./useLastUsedBtcAddress"
-export { default as useAcrePoints } from "./useAcrePoints"
export { default as useSignMessageAndCreateSession } from "./useSignMessageAndCreateSession"
-export { default as useScrollbarVisibility } from "./useScrollbarVisibility"
export { default as useAccessCode } from "./useAccessCode"
export { default as useFormField } from "./useFormField"
export { default as useDepositBTCTransaction } from "./useDepositBTCTransaction"
+export { default as useCancelPromise } from "./useCancelPromise"
+export { default as useActivitiesCount } from "./useActivitiesCount"
+export { default as useActivities } from "./useActivities"
+export { default as useBitcoinBalance } from "./useBitcoinBalance"
+export { default as useBitcoinPosition } from "./useBitcoinPosition"
+export { default as useMats } from "./useMats"
+export { default as useAcrePointsData } from "./useAcrePointsData"
+export { default as useUserPointsData } from "./useUserPointsData"
+export { default as useClaimPoints } from "./useClaimPoints"
diff --git a/dapp/src/hooks/orangeKit/index.ts b/dapp/src/hooks/orangeKit/index.ts
index 96fd2cb7a..dcc5160a5 100644
--- a/dapp/src/hooks/orangeKit/index.ts
+++ b/dapp/src/hooks/orangeKit/index.ts
@@ -4,4 +4,3 @@ export * from "./useConnectors"
export * from "./useAccountsChangedUnisat"
export * from "./useAccountsChangedOKX"
export * from "./useAccountChangedOKX"
-export { default as useBitcoinBalance } from "./useBitcoinBalance"
diff --git a/dapp/src/hooks/posthog/index.ts b/dapp/src/hooks/posthog/index.ts
new file mode 100644
index 000000000..1b0317599
--- /dev/null
+++ b/dapp/src/hooks/posthog/index.ts
@@ -0,0 +1,3 @@
+export * from "./usePostHogIdentity"
+export * from "./usePostHogCapture"
+export * from "./usePostHogPageViewCapture"
diff --git a/dapp/src/hooks/posthog/usePostHogCapture.ts b/dapp/src/hooks/posthog/usePostHogCapture.ts
new file mode 100644
index 000000000..d789691cf
--- /dev/null
+++ b/dapp/src/hooks/posthog/usePostHogCapture.ts
@@ -0,0 +1,40 @@
+import { PostHogEvent } from "#/posthog/events"
+import { PostHog, usePostHog } from "posthog-js/react"
+import { useCallback } from "react"
+
+type CaptureArgs = [
+ eventName: PostHogEvent,
+ ...rest: Parameters extends [unknown, ...infer R]
+ ? R
+ : never,
+]
+
+export const usePostHogCapture = () => {
+ const posthog = usePostHog()
+
+ const handleCapture = useCallback(
+ (...captureArgs: CaptureArgs) => {
+ posthog.capture(...captureArgs)
+ },
+ [posthog],
+ )
+
+ const handleCaptureWithCause = useCallback(
+ (error: unknown, ...captureArgs: CaptureArgs) => {
+ const [eventName, parameters, ...rest] = captureArgs
+
+ const captureParameters =
+ error instanceof Error
+ ? {
+ ...parameters,
+ cause: error.message,
+ }
+ : undefined
+
+ handleCapture(eventName, captureParameters, ...rest)
+ },
+ [handleCapture],
+ )
+
+ return { handleCapture, handleCaptureWithCause }
+}
diff --git a/dapp/src/hooks/posthog/usePostHogIdentity.ts b/dapp/src/hooks/posthog/usePostHogIdentity.ts
new file mode 100644
index 000000000..d4d977d21
--- /dev/null
+++ b/dapp/src/hooks/posthog/usePostHogIdentity.ts
@@ -0,0 +1,27 @@
+import { PostHog, usePostHog } from "posthog-js/react"
+import { useCallback } from "react"
+import { sha256, toUtf8Bytes } from "ethers"
+
+type IdentifyArgs = Parameters
+
+export const usePostHogIdentity = () => {
+ const posthog = usePostHog()
+
+ const handleIdentification = useCallback(
+ (...identifyArgs: IdentifyArgs) => {
+ const [id, ...rest] = identifyArgs
+ if (!id) return
+
+ const hashedId = sha256(toUtf8Bytes(id.toLowerCase())).slice(2, 12)
+
+ posthog.identify(hashedId, ...rest)
+ },
+ [posthog],
+ )
+
+ const resetIdentity = useCallback(() => {
+ posthog.reset()
+ }, [posthog])
+
+ return { handleIdentification, resetIdentity }
+}
diff --git a/dapp/src/hooks/posthog/usePostHogPageViewCapture.ts b/dapp/src/hooks/posthog/usePostHogPageViewCapture.ts
new file mode 100644
index 000000000..f1cae87e1
--- /dev/null
+++ b/dapp/src/hooks/posthog/usePostHogPageViewCapture.ts
@@ -0,0 +1,15 @@
+import { PostHogEvent } from "#/posthog/events"
+import { useEffect } from "react"
+import { useLocation } from "react-router-dom"
+import { usePostHogCapture } from "./usePostHogCapture"
+
+export const usePostHogPageViewCapture = () => {
+ const { handleCapture } = usePostHogCapture()
+ const location = useLocation()
+
+ useEffect(() => {
+ handleCapture(PostHogEvent.PageView)
+ }, [location, handleCapture])
+
+ return handleCapture
+}
diff --git a/dapp/src/hooks/router/index.ts b/dapp/src/hooks/router/index.ts
index 5b97eb715..c8edda5c3 100644
--- a/dapp/src/hooks/router/index.ts
+++ b/dapp/src/hooks/router/index.ts
@@ -1,2 +1 @@
-export * from "./useIsActiveRoute"
export { default as useAppNavigate } from "./useAppNavigate"
diff --git a/dapp/src/hooks/router/useIsActiveRoute.ts b/dapp/src/hooks/router/useIsActiveRoute.ts
deleted file mode 100644
index 715c3702a..000000000
--- a/dapp/src/hooks/router/useIsActiveRoute.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { routerPath } from "#/router/path"
-import { useLocation } from "react-router-dom"
-
-export const useIsActiveRoute = (route: string) => {
- const location = useLocation()
-
- return location.pathname === route
-}
-
-export const useIsHomeRouteActive = () => useIsActiveRoute(routerPath.home)
diff --git a/dapp/src/hooks/sdk/index.ts b/dapp/src/hooks/sdk/index.ts
index 0759a84ea..dca3d2ffa 100644
--- a/dapp/src/hooks/sdk/index.ts
+++ b/dapp/src/hooks/sdk/index.ts
@@ -1,6 +1,4 @@
export * from "./useInitializeAcreSdk"
export * from "./useFetchMinDepositAmount"
export * from "./useInitDataFromSdk"
-export * from "./useFetchActivities"
export * from "./useMinWithdrawAmount"
-export { default as useBitcoinPosition } from "./useBitcoinPosition"
diff --git a/dapp/src/hooks/sdk/useFetchActivities.ts b/dapp/src/hooks/sdk/useFetchActivities.ts
deleted file mode 100644
index 0d35bcbcc..000000000
--- a/dapp/src/hooks/sdk/useFetchActivities.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { useCallback } from "react"
-import { setActivities } from "#/store/wallet"
-import { useAcreContext } from "#/acre-react/hooks"
-import { Activity } from "#/types"
-import { DepositStatus } from "@acre-btc/sdk"
-import { useAppDispatch } from "../store/useAppDispatch"
-import { useWallet } from "../useWallet"
-
-export function useFetchActivities() {
- const dispatch = useAppDispatch()
- const { address } = useWallet()
- const { acre, isConnected } = useAcreContext()
-
- return useCallback(async () => {
- if (!acre || !isConnected || !address) return
-
- const deposits: Activity[] = (await acre.account.getDeposits()).map(
- (deposit) => ({
- ...deposit,
- status:
- deposit.status === DepositStatus.Finalized ? "completed" : "pending",
- type: "deposit",
- }),
- )
-
- const withdrawals: Activity[] = (await acre.account.getWithdrawals()).map(
- (withdraw) => {
- const { bitcoinTransactionId, status, ...rest } = withdraw
-
- return {
- ...rest,
- txHash: bitcoinTransactionId,
- status: status === "finalized" ? "completed" : "pending",
- type: "withdraw",
- }
- },
- )
-
- dispatch(setActivities([...deposits, ...withdrawals]))
- }, [acre, dispatch, isConnected, address])
-}
diff --git a/dapp/src/hooks/sdk/useInitDataFromSdk.ts b/dapp/src/hooks/sdk/useInitDataFromSdk.ts
index fdc19e82f..f912be723 100644
--- a/dapp/src/hooks/sdk/useInitDataFromSdk.ts
+++ b/dapp/src/hooks/sdk/useInitDataFromSdk.ts
@@ -1,24 +1,5 @@
-import { useEffect } from "react"
-import { useInterval } from "@chakra-ui/react"
-import { logPromiseFailure } from "#/utils"
-import { REFETCH_INTERVAL_IN_MILLISECONDS } from "#/constants"
import { useFetchMinDepositAmount } from "./useFetchMinDepositAmount"
-import { useFetchActivities } from "./useFetchActivities"
-import { useWallet } from "../useWallet"
export function useInitDataFromSdk() {
- const { address } = useWallet()
- const fetchActivities = useFetchActivities()
-
- useEffect(() => {
- if (address) {
- logPromiseFailure(fetchActivities())
- }
- }, [address, fetchActivities])
-
useFetchMinDepositAmount()
- useInterval(
- () => logPromiseFailure(fetchActivities()),
- REFETCH_INTERVAL_IN_MILLISECONDS,
- )
}
diff --git a/dapp/src/hooks/store/index.ts b/dapp/src/hooks/store/index.ts
index 75791ee54..b88527385 100644
--- a/dapp/src/hooks/store/index.ts
+++ b/dapp/src/hooks/store/index.ts
@@ -6,9 +6,6 @@ export * from "./useActionFlowStatus"
export * from "./useActionFlowActiveStep"
export * from "./useActionFlowTokenAmount"
export * from "./useActionFlowTxHash"
-export * from "./useAllActivitiesCount"
export * from "./useActionFlowPause"
export * from "./useIsSignedMessage"
-export { default as useHasFetchedActivities } from "./useHasFetchedActivities"
-export { default as useActivities } from "./useActivities"
export { default as useWalletAddress } from "./useWalletAddress"
diff --git a/dapp/src/hooks/store/useActivities.ts b/dapp/src/hooks/store/useActivities.ts
deleted file mode 100644
index b9f022b29..000000000
--- a/dapp/src/hooks/store/useActivities.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { selectActivities } from "#/store/wallet"
-import { useAppSelector } from "./useAppSelector"
-
-export default function useActivities() {
- return useAppSelector(selectActivities)
-}
diff --git a/dapp/src/hooks/store/useAllActivitiesCount.ts b/dapp/src/hooks/store/useAllActivitiesCount.ts
deleted file mode 100644
index cbf4e711f..000000000
--- a/dapp/src/hooks/store/useAllActivitiesCount.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { selectAllActivitiesCount } from "#/store/wallet"
-import { useAppSelector } from "./useAppSelector"
-
-export function useAllActivitiesCount() {
- return useAppSelector(selectAllActivitiesCount)
-}
diff --git a/dapp/src/hooks/store/useHasFetchedActivities.ts b/dapp/src/hooks/store/useHasFetchedActivities.ts
deleted file mode 100644
index 569978be9..000000000
--- a/dapp/src/hooks/store/useHasFetchedActivities.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { selectHasFetchedActivities } from "#/store/wallet"
-import { useAppSelector } from "./useAppSelector"
-
-function useHasFetchedActivities() {
- return useAppSelector(selectHasFetchedActivities)
-}
-
-export default useHasFetchedActivities
diff --git a/dapp/src/hooks/useAcrePoints.ts b/dapp/src/hooks/useAcrePoints.ts
deleted file mode 100644
index 81c329c14..000000000
--- a/dapp/src/hooks/useAcrePoints.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import { useMutation, useQuery } from "@tanstack/react-query"
-import { acreApi } from "#/utils"
-import { queryKeysFactory, REFETCH_INTERVAL_IN_MILLISECONDS } from "#/constants"
-import { MODAL_TYPES } from "#/types"
-import { useWallet } from "./useWallet"
-import { useModal } from "./useModal"
-
-const { userKeys, acreKeys } = queryKeysFactory
-
-type UseAcrePointsReturnType = {
- totalBalance: number
- claimableBalance: number
- nextDropTimestamp?: number
- isCalculationInProgress?: boolean
- claimPoints: () => void
- updateUserPointsData: () => Promise
- updatePointsData: () => Promise
- totalPoolBalance: number
-}
-
-export default function useAcrePoints(): UseAcrePointsReturnType {
- const { ethAddress = "" } = useWallet()
- const { openModal } = useModal()
-
- const userPointsDataQuery = useQuery({
- queryKey: [...userKeys.pointsData(), ethAddress],
- enabled: !!ethAddress,
- queryFn: async () => acreApi.getPointsDataByUser(ethAddress),
- })
-
- const pointsDataQuery = useQuery({
- queryKey: [...acreKeys.pointsData()],
- queryFn: async () => acreApi.getPointsData(),
- refetchInterval: REFETCH_INTERVAL_IN_MILLISECONDS,
- })
-
- const { mutate: claimPoints } = useMutation({
- mutationFn: async () => acreApi.claimPoints(ethAddress),
- onSettled: async () => {
- await userPointsDataQuery.refetch()
- },
- onSuccess: (data) => {
- const claimedAmount = Number(data.claimed)
- const totalAmount = Number(data.total)
-
- openModal(MODAL_TYPES.ACRE_POINTS_CLAIM, {
- claimedAmount,
- totalAmount,
- })
- },
- // TODO: Add the case when something goes wrong
- // onError: (error) => {},
- })
-
- return {
- totalBalance: userPointsDataQuery.data?.claimed || 0,
- claimableBalance: userPointsDataQuery.data?.unclaimed || 0,
- nextDropTimestamp: pointsDataQuery.data?.dropAt,
- isCalculationInProgress: pointsDataQuery.data?.isCalculationInProgress,
- claimPoints,
- updateUserPointsData: userPointsDataQuery.refetch,
- updatePointsData: pointsDataQuery.refetch,
- totalPoolBalance: pointsDataQuery.data?.totalPool || 0,
- }
-}
diff --git a/dapp/src/hooks/useAcrePointsData.ts b/dapp/src/hooks/useAcrePointsData.ts
new file mode 100644
index 000000000..f46474a27
--- /dev/null
+++ b/dapp/src/hooks/useAcrePointsData.ts
@@ -0,0 +1,13 @@
+import { queryKeysFactory, REFETCH_INTERVAL_IN_MILLISECONDS } from "#/constants"
+import { useQuery } from "@tanstack/react-query"
+import { acreApi } from "#/utils"
+
+const { acreKeys } = queryKeysFactory
+
+export default function useAcrePointsData() {
+ return useQuery({
+ queryKey: [...acreKeys.pointsData()],
+ queryFn: async () => acreApi.getPointsData(),
+ refetchInterval: REFETCH_INTERVAL_IN_MILLISECONDS,
+ })
+}
diff --git a/dapp/src/hooks/useActivities.ts b/dapp/src/hooks/useActivities.ts
new file mode 100644
index 000000000..adeb004fe
--- /dev/null
+++ b/dapp/src/hooks/useActivities.ts
@@ -0,0 +1,48 @@
+import { queryKeysFactory, REFETCH_INTERVAL_IN_MILLISECONDS } from "#/constants"
+import { useQuery } from "@tanstack/react-query"
+import { Activity } from "#/types"
+import { DepositStatus } from "@acre-btc/sdk"
+import { useAcreContext } from "#/acre-react/hooks"
+import { sortActivitiesByTimestamp } from "#/utils"
+import { useWallet } from "./useWallet"
+
+const { userKeys } = queryKeysFactory
+
+export default function useActivities() {
+ const { address } = useWallet()
+ const { acre, isConnected } = useAcreContext()
+
+ return useQuery({
+ queryKey: [...userKeys.activities(), { acre, isConnected, address }],
+ enabled: isConnected && !!acre && !!address,
+ queryFn: async () => {
+ if (!acre) return undefined
+
+ const deposits: Activity[] = (await acre.account.getDeposits()).map(
+ (deposit) => ({
+ ...deposit,
+ status:
+ deposit.status === DepositStatus.Finalized
+ ? "completed"
+ : "pending",
+ type: "deposit",
+ }),
+ )
+
+ const withdrawals: Activity[] = (await acre.account.getWithdrawals()).map(
+ (withdraw) => {
+ const { bitcoinTransactionId, status, ...rest } = withdraw
+
+ return {
+ ...rest,
+ txHash: bitcoinTransactionId,
+ status: status === "finalized" ? "completed" : "pending",
+ type: "withdraw",
+ }
+ },
+ )
+ return sortActivitiesByTimestamp([...deposits, ...withdrawals])
+ },
+ refetchInterval: REFETCH_INTERVAL_IN_MILLISECONDS,
+ })
+}
diff --git a/dapp/src/hooks/useActivitiesCount.ts b/dapp/src/hooks/useActivitiesCount.ts
new file mode 100644
index 000000000..a1ef7dba8
--- /dev/null
+++ b/dapp/src/hooks/useActivitiesCount.ts
@@ -0,0 +1,6 @@
+import useActivities from "./useActivities"
+
+export default function useActivitiesCount() {
+ const { data } = useActivities()
+ return data ? data.length : 0
+}
diff --git a/dapp/src/hooks/orangeKit/useBitcoinBalance.ts b/dapp/src/hooks/useBitcoinBalance.ts
similarity index 72%
rename from dapp/src/hooks/orangeKit/useBitcoinBalance.ts
rename to dapp/src/hooks/useBitcoinBalance.ts
index b7ed40eb8..d3f17aee6 100644
--- a/dapp/src/hooks/orangeKit/useBitcoinBalance.ts
+++ b/dapp/src/hooks/useBitcoinBalance.ts
@@ -1,10 +1,12 @@
import { useQuery } from "@tanstack/react-query"
import { REFETCH_INTERVAL_IN_MILLISECONDS, queryKeysFactory } from "#/constants"
-import { useBitcoinProvider } from "./useBitcoinProvider"
+import useWalletAddress from "./store/useWalletAddress"
+import { useBitcoinProvider } from "./orangeKit/useBitcoinProvider"
const { userKeys } = queryKeysFactory
-export default function useBitcoinBalance(address: string | undefined) {
+export default function useBitcoinBalance() {
+ const address = useWalletAddress()
const provider = useBitcoinProvider()
return useQuery({
diff --git a/dapp/src/hooks/sdk/useBitcoinPosition.ts b/dapp/src/hooks/useBitcoinPosition.ts
similarity index 95%
rename from dapp/src/hooks/sdk/useBitcoinPosition.ts
rename to dapp/src/hooks/useBitcoinPosition.ts
index f1036ef1a..8f208b958 100644
--- a/dapp/src/hooks/sdk/useBitcoinPosition.ts
+++ b/dapp/src/hooks/useBitcoinPosition.ts
@@ -1,7 +1,7 @@
import { useAcreContext } from "#/acre-react/hooks"
import { useQuery } from "@tanstack/react-query"
import { REFETCH_INTERVAL_IN_MILLISECONDS, queryKeysFactory } from "#/constants"
-import { useWallet } from "../useWallet"
+import { useWallet } from "./useWallet"
const { userKeys } = queryKeysFactory
diff --git a/dapp/src/hooks/useCancelPromise.ts b/dapp/src/hooks/useCancelPromise.ts
new file mode 100644
index 000000000..a6bb1666b
--- /dev/null
+++ b/dapp/src/hooks/useCancelPromise.ts
@@ -0,0 +1,50 @@
+import { useCallback, useEffect } from "react"
+
+const sessionIdToPromise: Record<
+ number,
+ {
+ promise: Promise
+ cancel: (reason: Error) => void
+ shouldOpenErrorModal: boolean
+ }
+> = {}
+
+export default function useCancelPromise(
+ sessionId: number,
+ errorMsgText: string,
+) {
+ useEffect(() => {
+ let cancel = (_: Error) => {}
+ const promise: Promise = new Promise((_, reject) => {
+ cancel = reject
+ })
+
+ sessionIdToPromise[sessionId] = {
+ cancel,
+ promise,
+ shouldOpenErrorModal: true,
+ }
+ }, [sessionId])
+
+ const cancel = useCallback(() => {
+ const sessionData = sessionIdToPromise[sessionId]
+ sessionIdToPromise[sessionId] = {
+ ...sessionData,
+ shouldOpenErrorModal: false,
+ }
+
+ sessionIdToPromise[sessionId].cancel(new Error(errorMsgText))
+ }, [errorMsgText, sessionId])
+
+ const resolve = useCallback(
+ () =>
+ Promise.race([sessionIdToPromise[sessionId].promise, Promise.resolve()]),
+ [sessionId],
+ )
+
+ return {
+ cancel,
+ resolve,
+ sessionIdToPromise,
+ }
+}
diff --git a/dapp/src/hooks/useClaimPoints.ts b/dapp/src/hooks/useClaimPoints.ts
new file mode 100644
index 000000000..0baca126f
--- /dev/null
+++ b/dapp/src/hooks/useClaimPoints.ts
@@ -0,0 +1,39 @@
+import { useMutation } from "@tanstack/react-query"
+import { acreApi } from "#/utils"
+import { MODAL_TYPES } from "#/types"
+import { PostHogEvent } from "#/posthog/events"
+import { useWallet } from "./useWallet"
+import { useModal } from "./useModal"
+import { usePostHogCapture } from "./posthog/usePostHogCapture"
+import useUserPointsData from "./useUserPointsData"
+
+export default function useClaimPoints() {
+ const { ethAddress = "" } = useWallet()
+ const { openModal } = useModal()
+ const { handleCapture, handleCaptureWithCause } = usePostHogCapture()
+ const { refetch: userPointsDataRefetch } = useUserPointsData()
+
+ return useMutation({
+ mutationFn: async () => acreApi.claimPoints(ethAddress),
+ onSettled: async () => {
+ await userPointsDataRefetch()
+ },
+ onSuccess: (data) => {
+ const claimedAmount = Number(data.claimed)
+ const totalAmount = Number(data.total)
+
+ openModal(MODAL_TYPES.ACRE_POINTS_CLAIM, {
+ claimedAmount,
+ totalAmount,
+ })
+
+ handleCapture(PostHogEvent.PointsClaimSuccess, {
+ claimedAmount,
+ totalAmount,
+ })
+ },
+ onError: (error) => {
+ handleCaptureWithCause(error, PostHogEvent.PointsClaimFailure)
+ },
+ })
+}
diff --git a/dapp/src/hooks/useDetectReferral.ts b/dapp/src/hooks/useDetectReferral.ts
deleted file mode 100644
index e32a457b6..000000000
--- a/dapp/src/hooks/useDetectReferral.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { useEffect } from "react"
-import useReferral from "./useReferral"
-
-export default function useDetectReferral() {
- const { detectReferral } = useReferral()
-
- useEffect(() => {
- detectReferral()
- }, [detectReferral])
-}
diff --git a/dapp/src/hooks/useDocsDrawer.ts b/dapp/src/hooks/useDocsDrawer.ts
deleted file mode 100644
index 3536dba2a..000000000
--- a/dapp/src/hooks/useDocsDrawer.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { useContext } from "react"
-import { DocsDrawerContext } from "#/contexts"
-
-export function useDocsDrawer() {
- const context = useContext(DocsDrawerContext)
-
- if (!context) {
- throw new Error(
- "DocsDrawerContext used outside of DocsDrawerContext component",
- )
- }
-
- return context
-}
diff --git a/dapp/src/hooks/useInitApp.ts b/dapp/src/hooks/useInitApp.ts
index e87e8955a..caf792595 100644
--- a/dapp/src/hooks/useInitApp.ts
+++ b/dapp/src/hooks/useInitApp.ts
@@ -4,7 +4,6 @@ import { useAccountsChangedOKX } from "./orangeKit/useAccountsChangedOKX"
import { useInitDataFromSdk, useInitializeAcreSdk } from "./sdk"
import { useSentry } from "./sentry"
import useDetectEmbed from "./useDetectEmbed"
-import useDetectReferral from "./useDetectReferral"
import { useDisconnectWallet } from "./useDisconnectWallet"
import { useFetchBTCPriceUSD } from "./useFetchBTCPriceUSD"
@@ -13,7 +12,6 @@ export function useInitApp() {
// useDetectThemeMode()
useSentry()
useDetectEmbed()
- useDetectReferral()
useInitializeAcreSdk()
useInitDataFromSdk()
useFetchBTCPriceUSD()
diff --git a/dapp/src/hooks/useInvalidateQueries.ts b/dapp/src/hooks/useInvalidateQueries.ts
deleted file mode 100644
index 2f64ae0f1..000000000
--- a/dapp/src/hooks/useInvalidateQueries.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { logPromiseFailure } from "#/utils"
-import { QueryClient, useQueryClient } from "@tanstack/react-query"
-import { useCallback } from "react"
-
-type InvalidateQueriesParams = Parameters
-
-export default function useInvalidateQueries(
- ...params: InvalidateQueriesParams
-) {
- const queryClient = useQueryClient()
-
- return useCallback(
- () => logPromiseFailure(queryClient.invalidateQueries(...params)),
- [params, queryClient],
- )
-}
diff --git a/dapp/src/hooks/useIsFetchedWalletData.ts b/dapp/src/hooks/useIsFetchedWalletData.ts
index 1d69280d4..0f2a99868 100644
--- a/dapp/src/hooks/useIsFetchedWalletData.ts
+++ b/dapp/src/hooks/useIsFetchedWalletData.ts
@@ -1,19 +1,21 @@
-import { queryKeysFactory } from "#/constants"
-import { useIsFetching } from "@tanstack/react-query"
-import { useHasFetchedActivities, useIsSignedMessage } from "./store"
-
-const { userKeys } = queryKeysFactory
+import { useIsSignedMessage } from "./store"
+import useActivities from "./useActivities"
+import useBitcoinBalance from "./useBitcoinBalance"
+import useBitcoinPosition from "./useBitcoinPosition"
+import useUserPointsData from "./useUserPointsData"
export default function useIsFetchedWalletData() {
const isSignedMessage = useIsSignedMessage()
- const hasFetchedActivities = useHasFetchedActivities()
- const fetchingQueries = useIsFetching({
- queryKey: userKeys.all,
- predicate: (query) => query.state.data === undefined,
- })
+ const { isFetched: isBitcoinBalanceFetched } = useBitcoinBalance()
+ const { isFetched: isBitcoinPositionFetched } = useBitcoinPosition()
+ const { isFetched: isActivitiesFetched } = useActivities()
+ const { isFetched: isPointsDataFetched } = useUserPointsData()
+
+ const isFetchedData =
+ isBitcoinBalanceFetched &&
+ isActivitiesFetched &&
+ isBitcoinPositionFetched &&
+ isPointsDataFetched
- return (
- (isSignedMessage && fetchingQueries === 0 && hasFetchedActivities) ||
- !isSignedMessage
- )
+ return (isSignedMessage && isFetchedData) || !isSignedMessage
}
diff --git a/dapp/src/hooks/useLocalStorage.ts b/dapp/src/hooks/useLocalStorage.ts
index d3d03e990..73272cecd 100644
--- a/dapp/src/hooks/useLocalStorage.ts
+++ b/dapp/src/hooks/useLocalStorage.ts
@@ -1,4 +1,7 @@
-import { useLocalStorage as useRehooksLocalStorage } from "@rehooks/local-storage"
+import {
+ useLocalStorage as useRehooksLocalStorage,
+ writeStorage,
+} from "@rehooks/local-storage"
export const parseLocalStorageValue = (value: string | null | undefined) => {
if (
@@ -12,6 +15,8 @@ export const parseLocalStorageValue = (value: string | null | undefined) => {
return value
}
+export { writeStorage }
+
export const getLocalStorageItem = (key: string): string | undefined => {
const value = localStorage.getItem(key)
return parseLocalStorageValue(value)
diff --git a/dapp/src/hooks/useReferral.ts b/dapp/src/hooks/useReferral.ts
index d919850e9..4e6f7566c 100644
--- a/dapp/src/hooks/useReferral.ts
+++ b/dapp/src/hooks/useReferral.ts
@@ -1,52 +1,23 @@
import { env } from "#/constants"
-import { referralProgram } from "#/utils"
-
import { useCallback, useMemo } from "react"
-import { MODAL_TYPES } from "#/types"
-import useIsEmbed from "./useIsEmbed"
-import useLocalStorage from "./useLocalStorage"
-import { useModal } from "./useModal"
+import useLocalStorage, { writeStorage } from "./useLocalStorage"
type UseReferralReturn = {
referral: number | null
- detectReferral: () => void
resetReferral: () => void
}
+const LOCAL_STORAGE_KEY = "acre.referral"
+
+export const writeReferral = (value: string) => {
+ writeStorage(LOCAL_STORAGE_KEY, value)
+}
+
export default function useReferral(): UseReferralReturn {
const [referral, setReferral] = useLocalStorage(
- "acre.referral",
+ LOCAL_STORAGE_KEY,
env.REFERRAL,
)
- const { openModal } = useModal()
- const { isEmbed, embeddedApp } = useIsEmbed()
-
- const detectReferral = useCallback(() => {
- const param = referralProgram.getReferralFromURL()
-
- if (isEmbed && embeddedApp) {
- setReferral(referralProgram.getReferralByEmbeddedApp(embeddedApp))
- return
- }
-
- if (param === null) {
- setReferral(env.REFERRAL)
- return
- }
-
- const convertedReferral = Number(param)
-
- if (referralProgram.isValidReferral(convertedReferral)) {
- setReferral(convertedReferral)
- } else {
- console.error("Incorrect referral")
- setReferral(null)
- openModal(MODAL_TYPES.UNEXPECTED_ERROR, {
- withCloseButton: false,
- closeOnEsc: false,
- })
- }
- }, [isEmbed, embeddedApp, openModal, setReferral])
const resetReferral = useCallback(() => {
setReferral(env.REFERRAL)
@@ -54,10 +25,9 @@ export default function useReferral(): UseReferralReturn {
return useMemo(
() => ({
- detectReferral,
resetReferral,
referral,
}),
- [detectReferral, resetReferral, referral],
+ [resetReferral, referral],
)
}
diff --git a/dapp/src/hooks/useScrollbarVisibility.ts b/dapp/src/hooks/useScrollbarVisibility.ts
deleted file mode 100644
index c1ce79034..000000000
--- a/dapp/src/hooks/useScrollbarVisibility.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { useCallback, useEffect, useState } from "react"
-
-const SCROLLBAR_WIDTH = `${window.innerWidth - document.body.offsetWidth}px`
-
-function isScrollbarVisible(selector: string) {
- const element = document.querySelector(selector)
-
- if (!element) return false
-
- return element?.scrollHeight > element?.clientHeight
-}
-
-export default function useScrollbarVisibility(selector: string) {
- const [isVisible, setIsVisible] = useState(false)
-
- useEffect(() => {
- const handleResize = () => {
- setIsVisible(isScrollbarVisible(selector))
- }
- window.addEventListener("resize", handleResize)
-
- return () => {
- window.removeEventListener("resize", handleResize)
- }
- }, [selector])
-
- const refreshState = useCallback(() => {
- setIsVisible(isScrollbarVisible(selector))
- }, [selector])
-
- return { isVisible, scrollbarWidth: SCROLLBAR_WIDTH, refreshState }
-}
diff --git a/dapp/src/hooks/useSidebar.ts b/dapp/src/hooks/useSidebar.ts
deleted file mode 100644
index 944364076..000000000
--- a/dapp/src/hooks/useSidebar.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { useContext } from "react"
-import { SidebarContext } from "#/contexts"
-
-export function useSidebar() {
- const context = useContext(SidebarContext)
-
- if (!context) {
- throw new Error("SidebarContext used outside of SidebarContext component")
- }
-
- return context
-}
diff --git a/dapp/src/hooks/useTransactionModal.ts b/dapp/src/hooks/useTransactionModal.ts
index e052ed131..7e4b9d0be 100644
--- a/dapp/src/hooks/useTransactionModal.ts
+++ b/dapp/src/hooks/useTransactionModal.ts
@@ -1,4 +1,4 @@
-import { ACTION_FLOW_TYPES, ActionFlowType, MODAL_TYPES } from "#/types"
+import { ActionFlowType, MODAL_TYPES } from "#/types"
import { useCallback } from "react"
import { useModal } from "./useModal"
@@ -8,7 +8,7 @@ export function useTransactionModal(type: ActionFlowType) {
return useCallback(() => {
openModal(MODAL_TYPES[type], {
type,
- closeOnEsc: type !== ACTION_FLOW_TYPES.UNSTAKE,
+ closeOnEsc: false,
})
}, [openModal, type])
}
diff --git a/dapp/src/hooks/useUserPointsData.ts b/dapp/src/hooks/useUserPointsData.ts
new file mode 100644
index 000000000..9e6e2cad2
--- /dev/null
+++ b/dapp/src/hooks/useUserPointsData.ts
@@ -0,0 +1,16 @@
+import { queryKeysFactory } from "#/constants"
+import { useQuery } from "@tanstack/react-query"
+import { acreApi } from "#/utils"
+import { useWallet } from "./useWallet"
+
+const { userKeys } = queryKeysFactory
+
+export default function useUserPointsData() {
+ const { ethAddress = "" } = useWallet()
+
+ return useQuery({
+ queryKey: [...userKeys.pointsData(), ethAddress],
+ enabled: !!ethAddress,
+ queryFn: async () => acreApi.getPointsDataByUser(ethAddress),
+ })
+}
diff --git a/dapp/src/hooks/useWallet.ts b/dapp/src/hooks/useWallet.ts
index 1462cfa3e..8125fd51a 100644
--- a/dapp/src/hooks/useWallet.ts
+++ b/dapp/src/hooks/useWallet.ts
@@ -8,6 +8,7 @@ import {
useDisconnect,
} from "wagmi"
import { orangeKit } from "#/utils"
+import sentry from "#/sentry"
import {
OnErrorCallback,
OrangeKitConnector,
@@ -17,10 +18,10 @@ import {
import { useMutation, useQueryClient } from "@tanstack/react-query"
import { useDispatch } from "react-redux"
import { setAddress } from "#/store/wallet"
-import useBitcoinBalance from "./orangeKit/useBitcoinBalance"
import useResetWalletState from "./useResetWalletState"
import useLastUsedBtcAddress from "./useLastUsedBtcAddress"
-import useWalletAddress from "./store/useWalletAddress"
+import useBitcoinBalance from "./useBitcoinBalance"
+import { useWalletAddress } from "./store"
const { typeConversionToConnector, typeConversionToOrangeKitConnector } =
orangeKit
@@ -50,7 +51,7 @@ export function useWallet(): UseWalletReturn {
const { setAddressInLocalStorage, removeAddressFromLocalStorage } =
useLastUsedBtcAddress()
- const { data: balance } = useBitcoinBalance(btcAddress)
+ const { data: balance } = useBitcoinBalance()
const chainId = useChainId()
const config = useConfig()
@@ -76,6 +77,7 @@ export function useWallet(): UseWalletReturn {
dispatch(setAddress(bitcoinAddress))
setAddressInLocalStorage(bitcoinAddress)
+ sentry.setUser(bitcoinAddress)
},
},
})
@@ -87,6 +89,7 @@ export function useWallet(): UseWalletReturn {
dispatch(setAddress(undefined))
removeAddressFromLocalStorage()
resetWalletState()
+ sentry.setUser(undefined)
},
},
})
@@ -125,6 +128,7 @@ export function useWallet(): UseWalletReturn {
dispatch(setAddress(bitcoinAddress))
setAddressInLocalStorage(bitcoinAddress)
+ sentry.setUser(bitcoinAddress)
},
},
queryClient,
diff --git a/dapp/src/hooks/useWalletConnectionAlert.ts b/dapp/src/hooks/useWalletConnectionAlert.ts
new file mode 100644
index 000000000..bab475294
--- /dev/null
+++ b/dapp/src/hooks/useWalletConnectionAlert.ts
@@ -0,0 +1,6 @@
+import { WalletConnectionAlertContext } from "#/contexts"
+import { useContext } from "react"
+
+export default function useWalletConnectionAlert() {
+ return useContext(WalletConnectionAlertContext)
+}
diff --git a/dapp/src/hooks/useWalletConnectionError.ts b/dapp/src/hooks/useWalletConnectionError.ts
deleted file mode 100644
index 55fae3a56..000000000
--- a/dapp/src/hooks/useWalletConnectionError.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { WalletConnectionErrorContext } from "#/contexts"
-import { useContext } from "react"
-
-export function useWalletConnectionError() {
- return useContext(WalletConnectionErrorContext)
-}
diff --git a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx
deleted file mode 100644
index 193c6c5d1..000000000
--- a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx
+++ /dev/null
@@ -1,141 +0,0 @@
-import React from "react"
-import { H4, TextMd } from "#/components/shared/Typography"
-import {
- Button,
- Card,
- CardBody,
- CardHeader,
- CardProps,
- HStack,
- Image,
- VStack,
-} from "@chakra-ui/react"
-import Countdown from "#/components/shared/Countdown"
-import { logPromiseFailure, numberToLocaleString } from "#/utils"
-import { useAcrePoints, useWallet } from "#/hooks"
-import Spinner from "#/components/shared/Spinner"
-import UserDataSkeleton from "#/components/shared/UserDataSkeleton"
-import InfoTooltip from "#/components/shared/InfoTooltip"
-import useDebounce from "#/hooks/useDebounce"
-import { ONE_SEC_IN_MILLISECONDS } from "#/constants"
-import acrePointsIllustrationSrc from "#/assets/images/acre-points-illustration.png"
-
-// TODO: Define colors as theme value
-const COLOR_TEXT_LIGHT_PRIMARY = "#1C1A16"
-const COLOR_TEXT_LIGHT_TERTIARY = "#7D6A4B"
-// TODO: Update `Button` component theme
-const COLOR_BUTTON_LABEL = "#FBF7EC"
-const COLOR_BUTTON_BACKGROUND = "#33A321"
-
-export default function AcrePointsCard(props: CardProps) {
- const {
- claimableBalance,
- nextDropTimestamp,
- totalBalance,
- claimPoints,
- updateUserPointsData,
- updatePointsData,
- isCalculationInProgress,
- totalPoolBalance,
- } = useAcrePoints()
- const { isConnected } = useWallet()
-
- const debouncedClaimPoints = useDebounce(claimPoints, ONE_SEC_IN_MILLISECONDS)
-
- const formattedTotalPointsAmount = numberToLocaleString(totalBalance)
- const formattedClaimablePointsAmount = numberToLocaleString(claimableBalance)
- const formattedTotalPoolBalance = numberToLocaleString(totalPoolBalance)
-
- const handleOnCountdownEnd = () => {
- logPromiseFailure(updatePointsData())
- logPromiseFailure(updateUserPointsData())
- }
-
- const isDataReady =
- isCalculationInProgress || !!nextDropTimestamp || !!claimableBalance
-
- return (
-
-
-
- {isConnected ? "Your" : "Total"} Acre points
-
-
-
-
-
-
-
-
- {isConnected
- ? formattedTotalPointsAmount
- : formattedTotalPoolBalance}
-
-
-
-
-
-
- {isDataReady && (
-
- {isCalculationInProgress ? (
-
- {!claimableBalance && (
- Please wait...
- )}
-
-
-
- Your drop is being prepared.
-
-
-
- ) : (
-
-
- Next drop in
-
-
-
- )}
-
- {claimableBalance && (
-
- Claim {formattedClaimablePointsAmount} PTS
-
- )}
-
- )}
-
-
-
- )
-}
diff --git a/dapp/src/pages/DashboardPage/AcrePointsCard/AcrePointsLabel.tsx b/dapp/src/pages/DashboardPage/AcrePointsCard/AcrePointsLabel.tsx
new file mode 100644
index 000000000..62797243c
--- /dev/null
+++ b/dapp/src/pages/DashboardPage/AcrePointsCard/AcrePointsLabel.tsx
@@ -0,0 +1,51 @@
+import React from "react"
+import { TextMd } from "#/components/shared/Typography"
+import { HStack } from "@chakra-ui/react"
+import Countdown from "#/components/shared/Countdown"
+import { logPromiseFailure } from "#/utils"
+import { useAcrePointsData, useUserPointsData } from "#/hooks"
+import LabelWrapper from "./LabelWrapper"
+
+// TODO: Define colors as theme value
+const COLOR_TEXT_LIGHT_PRIMARY = "#1C1A16"
+const COLOR_TEXT_LIGHT_TERTIARY = "#7D6A4B"
+
+export function NextDropTimestampLabel() {
+ const { data: acrePointsData, refetch: acrePointsDataRefetch } =
+ useAcrePointsData()
+ const { refetch: userPointsDataRefetch } = useUserPointsData()
+
+ const handleOnCountdownEnd = () => {
+ logPromiseFailure(acrePointsDataRefetch())
+ logPromiseFailure(userPointsDataRefetch())
+ }
+
+ if (!acrePointsData?.nextDropTimestamp) return null
+
+ return (
+
+
+ Next drop in
+
+
+
+ )
+}
+
+export default function AcrePointsLabel() {
+ const { data } = useAcrePointsData()
+
+ if (!data) return null
+
+ return (
+
+
+
+ )
+}
diff --git a/dapp/src/pages/DashboardPage/AcrePointsCard/LabelWrapper.tsx b/dapp/src/pages/DashboardPage/AcrePointsCard/LabelWrapper.tsx
new file mode 100644
index 000000000..77ea4c33d
--- /dev/null
+++ b/dapp/src/pages/DashboardPage/AcrePointsCard/LabelWrapper.tsx
@@ -0,0 +1,10 @@
+import React, { ReactNode } from "react"
+import { VStack } from "@chakra-ui/react"
+
+export default function LabelWrapper({ children }: { children: ReactNode }) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/dapp/src/pages/DashboardPage/AcrePointsCard/UserPointsLabel.tsx b/dapp/src/pages/DashboardPage/AcrePointsCard/UserPointsLabel.tsx
new file mode 100644
index 000000000..edae95326
--- /dev/null
+++ b/dapp/src/pages/DashboardPage/AcrePointsCard/UserPointsLabel.tsx
@@ -0,0 +1,88 @@
+import React from "react"
+import { TextMd } from "#/components/shared/Typography"
+import { Button, HStack, VStack } from "@chakra-ui/react"
+import { numberToLocaleString } from "#/utils"
+import { useAcrePointsData, useClaimPoints, useUserPointsData } from "#/hooks"
+import Spinner from "#/components/shared/Spinner"
+import TooltipIcon from "#/components/shared/TooltipIcon"
+import useDebounce from "#/hooks/useDebounce"
+import { ONE_SEC_IN_MILLISECONDS } from "#/constants"
+import LabelWrapper from "./LabelWrapper"
+import { NextDropTimestampLabel } from "./AcrePointsLabel"
+
+// TODO: Update `Button` component theme
+const COLOR_BUTTON_LABEL = "#FBF7EC"
+const COLOR_BUTTON_BACKGROUND = "#33A321"
+
+function ClaimableBalanceLabel() {
+ const { mutate: claimPoints } = useClaimPoints()
+ const { data: userPointsData } = useUserPointsData()
+ const debouncedClaimPoints = useDebounce(claimPoints, ONE_SEC_IN_MILLISECONDS)
+
+ const claimableBalance = userPointsData?.claimableBalance || 0
+ const formattedClaimablePointsAmount = numberToLocaleString(claimableBalance)
+
+ if (claimableBalance <= 0) return null
+
+ return (
+
+ Claim {formattedClaimablePointsAmount} PTS
+
+ )
+}
+
+function CalculationInProgressLabel() {
+ const { data } = useUserPointsData()
+
+ return (
+
+ {!data?.claimableBalance && (
+ Please wait...
+ )}
+
+
+ Your drop is being prepared.
+
+
+
+ )
+}
+
+export default function UserPointsLabel() {
+ const { data: acrePointsData } = useAcrePointsData()
+ const { data: userPointsData } = useUserPointsData()
+
+ if (acrePointsData || userPointsData?.isEligible) {
+ if (acrePointsData?.isCalculationInProgress)
+ return (
+
+
+
+
+ )
+
+ return (
+
+
+
+
+ )
+ }
+
+ return null
+}
diff --git a/dapp/src/pages/DashboardPage/AcrePointsCard/index.tsx b/dapp/src/pages/DashboardPage/AcrePointsCard/index.tsx
new file mode 100644
index 000000000..4e41ed8b3
--- /dev/null
+++ b/dapp/src/pages/DashboardPage/AcrePointsCard/index.tsx
@@ -0,0 +1,65 @@
+import React from "react"
+import { H4, TextMd } from "#/components/shared/Typography"
+import {
+ Card,
+ CardBody,
+ CardHeader,
+ CardProps,
+ HStack,
+ Image,
+} from "@chakra-ui/react"
+import { numberToLocaleString } from "#/utils"
+import { useAcrePointsData, useUserPointsData, useWallet } from "#/hooks"
+import UserDataSkeleton from "#/components/shared/UserDataSkeleton"
+import TooltipIcon from "#/components/shared/TooltipIcon"
+import acrePointsIllustrationSrc from "#/assets/images/acre-points-illustration.png"
+import AcrePointsLabel from "./AcrePointsLabel"
+import UserPointsLabel from "./UserPointsLabel"
+
+export default function AcrePointsCard(props: CardProps) {
+ const { data: acrePointsData } = useAcrePointsData()
+ const { data: userPointsData } = useUserPointsData()
+ const { isConnected } = useWallet()
+
+ const formattedUserTotalBalance = numberToLocaleString(
+ userPointsData?.totalBalance ?? 0,
+ )
+ const formattedTotalPoolBalance = numberToLocaleString(
+ acrePointsData?.totalPoolBalance ?? 0,
+ )
+
+ return (
+
+
+
+ {isConnected ? "Your" : "Total"} Acre points
+
+
+
+
+
+
+
+
+ {isConnected
+ ? formattedUserTotalBalance
+ : formattedTotalPoolBalance}
+
+
+
+
+
+
+ {isConnected ? : }
+
+
+
+ )
+}
diff --git a/dapp/src/pages/DashboardPage/AcreTVLMessage.tsx b/dapp/src/pages/DashboardPage/AcreTVLMessage.tsx
index 0960e95af..32b8e13a1 100644
--- a/dapp/src/pages/DashboardPage/AcreTVLMessage.tsx
+++ b/dapp/src/pages/DashboardPage/AcreTVLMessage.tsx
@@ -1,7 +1,7 @@
import React from "react"
import { Box, HStack, StackProps, VStack } from "@chakra-ui/react"
-import { useAllActivitiesCount, useStatistics, useWallet } from "#/hooks"
-import { BoltFilled } from "#/assets/icons"
+import { useActivitiesCount, useStatistics, useWallet } from "#/hooks"
+import { IconBolt } from "@tabler/icons-react"
import { TextMd } from "#/components/shared/Typography"
import { CurrencyBalance } from "#/components/shared/CurrencyBalance"
@@ -10,7 +10,7 @@ type AcreTVLMessageProps = Omit
export default function AcreTVLMessage(props: AcreTVLMessageProps) {
const { tvl } = useStatistics()
const { isConnected } = useWallet()
- const activitiesCount = useAllActivitiesCount()
+ const activitiesCount = useActivitiesCount()
const isFirstTimeUser = activitiesCount === 0
@@ -20,7 +20,9 @@ export default function AcreTVLMessage(props: AcreTVLMessageProps) {
return (
-
+
+
+
{tvl.isCapExceeded ? (
diff --git a/dapp/src/pages/DashboardPage/BeehiveCard.tsx b/dapp/src/pages/DashboardPage/BeehiveCard.tsx
index 9168d0b5b..d768e94d7 100644
--- a/dapp/src/pages/DashboardPage/BeehiveCard.tsx
+++ b/dapp/src/pages/DashboardPage/BeehiveCard.tsx
@@ -1,5 +1,12 @@
import React from "react"
+import { MezoSignIcon } from "#/assets/icons"
+import beehiveIllustrationSrc from "#/assets/images/beehive-illustration.svg"
+import TooltipIcon from "#/components/shared/TooltipIcon"
import { H6, TextMd, TextSm } from "#/components/shared/Typography"
+import UserDataSkeleton from "#/components/shared/UserDataSkeleton"
+import { useMats, useModal } from "#/hooks"
+import { MODAL_TYPES } from "#/types"
+import { numberToLocaleString } from "#/utils"
import {
Box,
Button,
@@ -12,13 +19,6 @@ import {
Image,
VStack,
} from "@chakra-ui/react"
-import { MezoSignIcon } from "#/assets/icons"
-import { useMats, useModal } from "#/hooks"
-import { MODAL_TYPES } from "#/types"
-import beehiveIllustrationSrc from "#/assets/images/beehive-illustration.svg"
-import UserDataSkeleton from "#/components/shared/UserDataSkeleton"
-import { numberToLocaleString } from "#/utils"
-import InfoTooltip from "#/components/shared/InfoTooltip"
export default function BeehiveCard(props: CardProps) {
const { openModal } = useModal()
@@ -32,7 +32,7 @@ export default function BeehiveCard(props: CardProps) {
Additional rewards
-
diff --git a/dapp/src/pages/DashboardPage/GrantedSeasonPassCard.tsx b/dapp/src/pages/DashboardPage/GrantedSeasonPassCard.tsx
deleted file mode 100644
index 4e618844f..000000000
--- a/dapp/src/pages/DashboardPage/GrantedSeasonPassCard.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import React from "react"
-import {
- Card,
- CardBody,
- CardHeader,
- CardProps,
- HStack,
- Icon,
-} from "@chakra-ui/react"
-import { IconDiscountCheckFilled, IconLock } from "@tabler/icons-react"
-import { TextMd } from "#/components/shared/Typography"
-import UserDataSkeleton from "#/components/shared/UserDataSkeleton"
-
-export default function GrantedSeasonPassCard(props: CardProps) {
- return (
-
-
-
- Season 2 arriving soon
-
-
-
-
-
-
- Your seat is secured.
-
-
-
- )
-}
diff --git a/dapp/src/pages/DashboardPage/PositionDetails.tsx b/dapp/src/pages/DashboardPage/PositionDetails.tsx
index 8346fcc8a..6b4f4267e 100644
--- a/dapp/src/pages/DashboardPage/PositionDetails.tsx
+++ b/dapp/src/pages/DashboardPage/PositionDetails.tsx
@@ -1,26 +1,23 @@
import React from "react"
import { CurrencyBalanceWithConversion } from "#/components/shared/CurrencyBalanceWithConversion"
import {
- useAllActivitiesCount,
+ useActivitiesCount,
useBitcoinPosition,
useTransactionModal,
useStatistics,
useWallet,
useMobileMode,
+ useActivities,
} from "#/hooks"
import { ACTION_FLOW_TYPES } from "#/types"
-import {
- Button,
- ButtonProps,
- Flex,
- HStack,
- // Tag,
- VStack,
-} from "@chakra-ui/react"
+import { Button, ButtonProps, Flex, HStack, VStack } from "@chakra-ui/react"
import ArrivingSoonTooltip from "#/components/ArrivingSoonTooltip"
import UserDataSkeleton from "#/components/shared/UserDataSkeleton"
import { featureFlags } from "#/constants"
import { TextMd } from "#/components/shared/Typography"
+import { IconClockHour5Filled } from "@tabler/icons-react"
+import TooltipIcon from "#/components/shared/TooltipIcon"
+import { hasPendingDeposits } from "#/utils"
import AcreTVLMessage from "./AcreTVLMessage"
const isWithdrawalFlowEnabled = featureFlags.WITHDRAWALS_ENABLED
@@ -36,12 +33,13 @@ const buttonStyles: ButtonProps = {
}
export default function PositionDetails() {
- const { data } = useBitcoinPosition()
- const bitcoinAmount = data?.estimatedBitcoinBalance ?? 0n
+ const { data: bitcoinPosition } = useBitcoinPosition()
+ const bitcoinAmount = bitcoinPosition?.estimatedBitcoinBalance ?? 0n
const openDepositModal = useTransactionModal(ACTION_FLOW_TYPES.STAKE)
const openWithdrawModal = useTransactionModal(ACTION_FLOW_TYPES.UNSTAKE)
- const activitiesCount = useAllActivitiesCount()
+ const activitiesCount = useActivitiesCount()
+ const { data: activities } = useActivities()
const isMobileMode = useMobileMode()
const { tvl } = useStatistics()
@@ -55,25 +53,16 @@ export default function PositionDetails() {
{/* TODO: Component should be moved to `CardHeader` */}
-
- Your deposit
- {/* TODO: Uncomment when position will be implemented */}
- {/* {positionPercentage && (
-
- Top {positionPercentage}%
-
- )} */}
-
+
+ Your Acre balance
+ {hasPendingDeposits(activities ?? []) && (
+
+ )}
+
+
{(pageData: Activity[]) =>
pageData.map((activity) => (
diff --git a/dapp/src/pages/DashboardPage/TransactionHistory/index.tsx b/dapp/src/pages/DashboardPage/TransactionHistory/index.tsx
index 960d7f363..539dffdf3 100644
--- a/dapp/src/pages/DashboardPage/TransactionHistory/index.tsx
+++ b/dapp/src/pages/DashboardPage/TransactionHistory/index.tsx
@@ -1,13 +1,13 @@
import React from "react"
import { StackProps, VStack, Image } from "@chakra-ui/react"
import { TextMd } from "#/components/shared/Typography"
-import { useAllActivitiesCount, useIsFetchedWalletData } from "#/hooks"
+import { useActivitiesCount, useIsFetchedWalletData } from "#/hooks"
import UserDataSkeleton from "#/components/shared/UserDataSkeleton"
import emptyStateIllustration from "#/assets/images/empty-state.svg"
import TransactionTable from "./TransactionTable"
function TransactionHistoryContent() {
- const activitiesCount = useAllActivitiesCount()
+ const activitiesCount = useActivitiesCount()
const isFetchedWalletData = useIsFetchedWalletData()
if (!isFetchedWalletData)
diff --git a/dapp/src/pages/DashboardPage/UsefulLinks.tsx b/dapp/src/pages/DashboardPage/UsefulLinks.tsx
deleted file mode 100644
index 6202c411f..000000000
--- a/dapp/src/pages/DashboardPage/UsefulLinks.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import React from "react"
-import { StackProps, VStack } from "@chakra-ui/react"
-import ButtonLink from "#/components/shared/ButtonLink"
-import { EXTERNAL_HREF } from "#/constants"
-
-export default function UsefulLinks(props: StackProps) {
- return (
-
- {[
- { label: "Documentation", href: EXTERNAL_HREF.DOCS },
- { label: "Blog", href: EXTERNAL_HREF.BLOG },
- { label: "FAQ", href: EXTERNAL_HREF.FAQ },
- ].map(({ label, href }) => (
-
- {label}
-
- ))}
-
- )
-}
diff --git a/dapp/src/pages/DashboardPage/index.tsx b/dapp/src/pages/DashboardPage/index.tsx
index e0fb11bc2..b35928aa6 100644
--- a/dapp/src/pages/DashboardPage/index.tsx
+++ b/dapp/src/pages/DashboardPage/index.tsx
@@ -3,7 +3,6 @@ import { featureFlags } from "#/constants"
import { useTriggerConnectWalletModal } from "#/hooks"
import { Grid } from "@chakra-ui/react"
import DashboardCard from "./DashboardCard"
-// import GrantedSeasonPassCard from "./GrantedSeasonPassCard"
import AcrePointsCard from "./AcrePointsCard"
import AcrePointsTemplateCard from "./AcrePointsTemplateCard"
import BeehiveCard from "./BeehiveCard"
@@ -52,9 +51,6 @@ export default function DashboardPage() {
- {/* TODO: Uncomment in post-launch phases + add `gridArea` and update `templateAreas` */}
- {/* */}
-
{featureFlags.ACRE_POINTS_ENABLED ? (
) : (
diff --git a/dapp/src/posthog/PostHogProvider.tsx b/dapp/src/posthog/PostHogProvider.tsx
new file mode 100644
index 000000000..39e733fee
--- /dev/null
+++ b/dapp/src/posthog/PostHogProvider.tsx
@@ -0,0 +1,26 @@
+import React, { PropsWithChildren } from "react"
+import { PostHogProvider as Provider } from "posthog-js/react"
+import { PostHogConfig } from "posthog-js"
+import { featureFlags, env } from "../constants"
+
+const options: Partial = {
+ api_host: env.POSTHOG_API_HOST,
+ capture_pageview: false,
+ persistence: "memory",
+}
+
+function PostHogProvider(props: PropsWithChildren) {
+ const { children } = props
+
+ if (!featureFlags.POSTHOG_ENABLED) {
+ return children
+ }
+
+ return (
+
+ {children}
+
+ )
+}
+
+export default PostHogProvider
diff --git a/dapp/src/posthog/events.ts b/dapp/src/posthog/events.ts
new file mode 100644
index 000000000..992b9958e
--- /dev/null
+++ b/dapp/src/posthog/events.ts
@@ -0,0 +1,9 @@
+export enum PostHogEvent {
+ PageView = "$pageview",
+ DepositSuccess = "deposit_success",
+ DepositFailure = "deposit_failure",
+ WithdrawalSuccess = "withdrawal_success",
+ WithdrawalFailure = "withdrawal_failure",
+ PointsClaimSuccess = "points_claim_success",
+ PointsClaimFailure = "points_claim_failure",
+}
diff --git a/dapp/src/router/index.tsx b/dapp/src/router/index.tsx
index 9d6a42b0f..351a8504e 100644
--- a/dapp/src/router/index.tsx
+++ b/dapp/src/router/index.tsx
@@ -12,6 +12,8 @@ import AccessPage from "#/pages/AccessPage"
import WelcomePage from "#/pages/WelcomePage"
import welcomePageLoader from "#/pages/WelcomePage/loader"
import accessPageLoader from "#/pages/AccessPage/loader"
+import { writeReferral } from "#/hooks/useReferral"
+import { env } from "#/constants"
import { routerPath } from "./path"
const mainLayoutLoader: LoaderFunction = ({ request }) => {
@@ -32,6 +34,24 @@ export const router = createBrowserRouter([
{
path: "/",
element: ,
+ loader: ({ request }) => {
+ // TODO: display the error page/modal when the referral is invalid.
+ const referralCodeFromUrl = referralProgram.getReferralFromURL()
+
+ const referralCode = referralProgram.isValidReferral(referralCodeFromUrl)
+ ? referralCodeFromUrl!
+ : env.REFERRAL
+
+ writeReferral(referralCode.toString())
+
+ const embedApp = referralProgram.getEmbeddedApp(request.url)
+ if (referralProgram.isEmbedApp(embedApp)) {
+ writeReferral(
+ referralProgram.getReferralByEmbeddedApp(embedApp).toString(),
+ )
+ }
+ return null
+ },
children: [
{
index: true,
diff --git a/dapp/src/sentry.ts b/dapp/src/sentry.ts
new file mode 100644
index 000000000..f3707ede4
--- /dev/null
+++ b/dapp/src/sentry.ts
@@ -0,0 +1,50 @@
+import * as Sentry from "@sentry/react"
+import { sha256, toUtf8Bytes } from "ethers"
+
+const initialize = (dsn: string) => {
+ Sentry.init({
+ dsn,
+ integrations: [
+ Sentry.browserTracingIntegration(),
+ Sentry.captureConsoleIntegration({ levels: ["error"] }),
+ Sentry.extraErrorDataIntegration(),
+ Sentry.httpClientIntegration(),
+ ],
+ // Attach stacktrace to errors logged by `console.error`. This is useful for
+ // the `captureConsoleIntegration` integration.
+ attachStacktrace: true,
+ // Performance Monitoring
+ tracesSampleRate: 0.1,
+ })
+}
+
+/**
+ * Sets the user in Sentry with an ID from hashed Bitcoin address.
+ * The Bitcoin address is first converted to lowercase and then hashed using SHA-256.
+ * The resulting hash is then converted to a hexadecimal string and the first 10
+ * characters are set as the user ID.
+ *
+ * @param bitcoinAddress - The Bitcoin address of the user. If undefined, the user
+ * is set to null in Sentry.
+ */
+const setUser = (bitcoinAddress: string | undefined) => {
+ if (!bitcoinAddress) {
+ Sentry.setUser(null)
+ return
+ }
+
+ const hashedBitcoinAddress = sha256(toUtf8Bytes(bitcoinAddress.toLowerCase()))
+ // Remove the 0x prefix and take the first 10 characters.
+ const id = hashedBitcoinAddress.slice(2, 12)
+
+ Sentry.setUser({ id })
+}
+
+const captureException = (exception: unknown) =>
+ Sentry.captureException(exception)
+
+export default {
+ initialize,
+ setUser,
+ captureException,
+}
diff --git a/dapp/src/sentry/index.ts b/dapp/src/sentry/index.ts
deleted file mode 100644
index 0bb08159d..000000000
--- a/dapp/src/sentry/index.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import * as Sentry from "@sentry/react"
-
-const initialize = (dsn: string) => {
- Sentry.init({
- dsn,
- integrations: [
- Sentry.browserTracingIntegration(),
- Sentry.captureConsoleIntegration({ levels: ["error"] }),
- ],
- // Attach stacktrace to errors logged by `console.error`. This is useful for
- // the `captureConsoleIntegration` integration.
- attachStacktrace: true,
- // Performance Monitoring
- tracesSampleRate: 0.1,
- })
-}
-
-const captureException = (exception: unknown) =>
- Sentry.captureException(exception)
-
-export default {
- initialize,
- captureException,
-}
diff --git a/dapp/src/store/tests/walletSlice.test.ts b/dapp/src/store/tests/walletSlice.test.ts
deleted file mode 100644
index b29009f60..000000000
--- a/dapp/src/store/tests/walletSlice.test.ts
+++ /dev/null
@@ -1,146 +0,0 @@
-import { beforeEach, describe, expect, it } from "vitest"
-import { createActivity } from "#/tests/factories"
-import { Activity } from "#/types"
-import { WalletState } from "../wallet"
-import reducer, { initialState, setActivities } from "../wallet/walletSlice"
-
-const isSignedMessage = false
-const hasFetchedActivities = true
-const pendingActivityId = "0"
-const pendingActivity = createActivity({
- id: pendingActivityId,
- status: "pending",
-})
-
-const activities = [
- pendingActivity,
- createActivity({ id: "1" }),
- createActivity({ id: "2" }),
-]
-
-describe("Wallet redux slice", () => {
- describe("deposits", () => {
- let state: WalletState
-
- beforeEach(() => {
- state = {
- ...initialState,
- activities,
- isSignedMessage,
- hasFetchedActivities,
- }
- })
-
- it("should update activities when the status of item changes", () => {
- const newActivities = [...activities]
- const completedActivity: Activity = {
- ...pendingActivity,
- status: "completed",
- }
- const foundIndex = newActivities.findIndex(
- ({ id }) => id === pendingActivityId,
- )
- newActivities[foundIndex] = completedActivity
-
- expect(reducer(state, setActivities(newActivities))).toEqual({
- ...initialState,
- activities: newActivities,
- isSignedMessage,
- hasFetchedActivities,
- })
- })
- })
-
- describe("withdrawals", () => {
- let state: WalletState
- const pendingWithdrawRedemptionKey =
- "0x047078deab9f2325ce5adc483d6b28dfb32547017ffb73f857482b51b622d5eb"
- const pendingWithdrawActivity = createActivity({
- // After the successful withdrawal flow we set the id to redemption key
- // w/o the `-` suffix because it's hard to get the exact number of
- // withdrawals with the same redemption key. There can only be one pending
- // withdrawal with the same redemption key at a time.
- id: pendingWithdrawRedemptionKey,
- status: "pending",
- type: "withdraw",
- })
-
- // Let's assume the user has already made 2 withdrawals and these 2
- // withdrawals have the same redemption key as the newly created. Both are
- // completed.
- const currentActivities = [
- createActivity({
- type: "withdraw",
- id: `${pendingWithdrawRedemptionKey}-1`,
- }),
- createActivity({
- type: "withdraw",
- id: `${pendingWithdrawRedemptionKey}-2`,
- }),
- ]
-
- describe("when withdrawal is still pending", () => {
- // This is our pending withdrawal but with the full id with the `-`
- // suffix returned by backend.
- const pendingWithdrawActivityWithFullId = {
- ...pendingWithdrawActivity,
- id: `${pendingWithdrawRedemptionKey}-3`,
- }
- // The new data returned from the backend and they includes our pending
- // withdrawal.
- const newActivities = [
- ...currentActivities,
- pendingWithdrawActivityWithFullId,
- ]
-
- beforeEach(() => {
- state = {
- ...initialState,
- activities: currentActivities,
- isSignedMessage,
- hasFetchedActivities,
- }
- })
-
- it("should not update pending withdraw state and should set correct id", () => {
- expect(reducer(state, setActivities(newActivities))).toEqual({
- ...initialState,
- activities: newActivities,
- isSignedMessage,
- hasFetchedActivities,
- })
- })
- })
-
- describe("when withdrawal is already complete", () => {
- const withdrawActivityCompleted: Activity = {
- ...pendingWithdrawActivity,
- status: "completed",
- id: `${pendingWithdrawRedemptionKey}-3`,
- }
-
- // Let's assume the pending withdrawal is already completed and the
- // backend returns it but with the full id. Note that the pending activity
- // is still in the `latestActivities` map but w/o the full id (id is
- // redemption key).
- const newActivities = [...currentActivities, withdrawActivityCompleted]
-
- beforeEach(() => {
- state = {
- ...initialState,
- activities: currentActivities,
- isSignedMessage,
- }
- })
-
- it("should mark the latest pending withdraw activity as completed", () => {
- expect(reducer(state, setActivities(newActivities))).toEqual({
- ...initialState,
- activities: newActivities,
- isSignedMessage,
- hasFetchedActivities,
- })
- })
- })
- })
-})
diff --git a/dapp/src/store/wallet/walletSelector.ts b/dapp/src/store/wallet/walletSelector.ts
index db8194cd6..9df077205 100644
--- a/dapp/src/store/wallet/walletSelector.ts
+++ b/dapp/src/store/wallet/walletSelector.ts
@@ -1,5 +1,3 @@
-import { createSelector } from "@reduxjs/toolkit"
-import { sortActivitiesByTimestamp } from "#/utils"
import { RootState } from ".."
export const selectEstimatedBtcBalance = (state: RootState): bigint =>
@@ -8,21 +6,8 @@ export const selectEstimatedBtcBalance = (state: RootState): bigint =>
export const selectSharesBalance = (state: RootState): bigint =>
state.wallet.sharesBalance
-export const selectActivities = createSelector(
- (state: RootState) => state.wallet.activities,
- (activities) => sortActivitiesByTimestamp(activities),
-)
-
-export const selectAllActivitiesCount = createSelector(
- (state: RootState) => state.wallet.activities,
- (activities) => activities.length,
-)
-
export const selectIsSignedMessage = (state: RootState): boolean =>
state.wallet.isSignedMessage
-export const selectHasFetchedActivities = (state: RootState): boolean =>
- state.wallet.hasFetchedActivities
-
export const selectWalletAddress = (state: RootState): string | undefined =>
state.wallet.address
diff --git a/dapp/src/store/wallet/walletSlice.ts b/dapp/src/store/wallet/walletSlice.ts
index fb0296445..a067fbcca 100644
--- a/dapp/src/store/wallet/walletSlice.ts
+++ b/dapp/src/store/wallet/walletSlice.ts
@@ -1,12 +1,9 @@
-import { Activity } from "#/types"
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
export type WalletState = {
estimatedBtcBalance: bigint
sharesBalance: bigint
isSignedMessage: boolean
- activities: Activity[]
- hasFetchedActivities: boolean
address: string | undefined
}
@@ -14,8 +11,6 @@ export const initialState: WalletState = {
estimatedBtcBalance: 0n,
sharesBalance: 0n,
isSignedMessage: false,
- activities: [],
- hasFetchedActivities: false,
address: undefined,
}
@@ -32,15 +27,7 @@ export const walletSlice = createSlice({
setIsSignedMessage(state, action: PayloadAction) {
state.isSignedMessage = action.payload
},
- setActivities(state, action: PayloadAction) {
- state.activities = action.payload
- state.hasFetchedActivities = true
- },
resetState: (state) => ({ ...initialState, address: state.address }),
- activityInitialized(state, action: PayloadAction) {
- const activity = action.payload
- state.activities = [...state.activities, activity]
- },
setAddress(state, action: PayloadAction) {
state.address = action.payload
},
@@ -51,9 +38,7 @@ export const {
setSharesBalance,
setEstimatedBtcBalance,
setIsSignedMessage,
- setActivities,
resetState,
- activityInitialized,
setAddress,
} = walletSlice.actions
export default walletSlice.reducer
diff --git a/dapp/src/tests/factories.ts b/dapp/src/tests/factories.ts
deleted file mode 100644
index 8f3d66c41..000000000
--- a/dapp/src/tests/factories.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { Activity } from "#/types"
-import { dateToUnixTimestamp, randomInteger } from "#/utils"
-
-export const createActivity = (
- overrides: Partial = {},
-): Activity => ({
- id: crypto.randomUUID(),
- initializedAt: dateToUnixTimestamp() - randomInteger(0, 1000000),
- amount: BigInt(randomInteger(1000000, 1000000000)),
- txHash: "c9625ecc138bbd241439f158f65f43e152968ff35e203dec89cfb78237d6a2d8",
- status: "completed",
- type: "deposit",
- ...overrides,
-})
diff --git a/dapp/src/theme/Drawer.ts b/dapp/src/theme/Drawer.ts
deleted file mode 100644
index b904c74c0..000000000
--- a/dapp/src/theme/Drawer.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { drawerAnatomy as parts } from "@chakra-ui/anatomy"
-import { createMultiStyleConfigHelpers, defineStyle } from "@chakra-ui/react"
-
-const baseStyleDialogContainer = defineStyle({
- zIndex: "drawer",
-})
-
-const baseStyleDialog = defineStyle({
- borderTop: "2px",
- borderLeft: "2px",
- boxShadow: "none",
- borderColor: "white",
- borderTopLeftRadius: "xl",
- bg: "gold.100",
-})
-
-const baseStyleOverlay = defineStyle({
- bg: "none",
- backdropFilter: "auto",
- backdropBlur: "8px",
-})
-
-const multiStyleConfig = createMultiStyleConfigHelpers(parts.keys)
-
-const baseStyle = multiStyleConfig.definePartsStyle({
- dialogContainer: baseStyleDialogContainer,
- dialog: baseStyleDialog,
- overlay: baseStyleOverlay,
-})
-
-export const drawerTheme = multiStyleConfig.defineMultiStyleConfig({
- baseStyle,
-})
diff --git a/dapp/src/theme/Sidebar.ts b/dapp/src/theme/Sidebar.ts
deleted file mode 100644
index 1b1a431b1..000000000
--- a/dapp/src/theme/Sidebar.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { createMultiStyleConfigHelpers, defineStyle } from "@chakra-ui/react"
-
-const PARTS = ["sidebarContainer", "sidebar"]
-
-const baseStyleSidebarContainer = defineStyle({
- top: 0,
- right: 0,
- h: "100vh",
- position: "fixed",
- overflow: "hidden",
- zIndex: "sidebar",
- transition: "width 0.3s",
-})
-
-const baseStyleSidebar = defineStyle({
- p: 4,
- height: "100%",
- w: "sidebar_width",
- bg: "gold.200",
- borderTop: "2px",
- borderLeft: "2px",
- borderColor: "gold.100",
- borderTopLeftRadius: "xl",
- display: "flex",
- flexDirection: "column",
- gap: 3,
-})
-
-const multiStyleConfig = createMultiStyleConfigHelpers(PARTS)
-
-const baseStyle = multiStyleConfig.definePartsStyle({
- sidebarContainer: baseStyleSidebarContainer,
- sidebar: baseStyleSidebar,
-})
-
-export const sidebarTheme = multiStyleConfig.defineMultiStyleConfig({
- baseStyle,
-})
diff --git a/dapp/src/theme/AcreTVLProgress.ts b/dapp/src/theme/acreTVLProgressTheme.ts
similarity index 100%
rename from dapp/src/theme/AcreTVLProgress.ts
rename to dapp/src/theme/acreTVLProgressTheme.ts
diff --git a/dapp/src/theme/Alert.ts b/dapp/src/theme/alertTheme.ts
similarity index 96%
rename from dapp/src/theme/Alert.ts
rename to dapp/src/theme/alertTheme.ts
index 8b4aa91f9..905c63e97 100644
--- a/dapp/src/theme/Alert.ts
+++ b/dapp/src/theme/alertTheme.ts
@@ -12,10 +12,14 @@ const baseStyle = multiStyleConfig.definePartsStyle({
borderWidth: 1,
borderStyle: "solid",
borderColor: $borderColor.reference,
- px: 5,
- py: 5,
+ p: 4,
rounded: "xl",
},
+ title: {
+ fontWeight: "semibold",
+ mr: 0,
+ },
+
description: {
fontWeight: "medium",
textAlign: "start",
diff --git a/dapp/src/theme/Button.ts b/dapp/src/theme/buttonTheme.ts
similarity index 100%
rename from dapp/src/theme/Button.ts
rename to dapp/src/theme/buttonTheme.ts
diff --git a/dapp/src/theme/Card.ts b/dapp/src/theme/cardTheme.ts
similarity index 100%
rename from dapp/src/theme/Card.ts
rename to dapp/src/theme/cardTheme.ts
diff --git a/dapp/src/theme/CloseButton.ts b/dapp/src/theme/closeButtonTheme.ts
similarity index 100%
rename from dapp/src/theme/CloseButton.ts
rename to dapp/src/theme/closeButtonTheme.ts
diff --git a/dapp/src/theme/Countdown.ts b/dapp/src/theme/countdownTheme.ts
similarity index 100%
rename from dapp/src/theme/Countdown.ts
rename to dapp/src/theme/countdownTheme.ts
diff --git a/dapp/src/theme/CurrencyBalance.ts b/dapp/src/theme/currencyBalanceTheme.ts
similarity index 100%
rename from dapp/src/theme/CurrencyBalance.ts
rename to dapp/src/theme/currencyBalanceTheme.ts
diff --git a/dapp/src/theme/Footer.ts b/dapp/src/theme/footerTheme.ts
similarity index 100%
rename from dapp/src/theme/Footer.ts
rename to dapp/src/theme/footerTheme.ts
diff --git a/dapp/src/theme/FormError.ts b/dapp/src/theme/formErrorTheme.ts
similarity index 100%
rename from dapp/src/theme/FormError.ts
rename to dapp/src/theme/formErrorTheme.ts
diff --git a/dapp/src/theme/FormLabel.ts b/dapp/src/theme/formLabelTheme.ts
similarity index 100%
rename from dapp/src/theme/FormLabel.ts
rename to dapp/src/theme/formLabelTheme.ts
diff --git a/dapp/src/theme/Form.ts b/dapp/src/theme/formTheme.ts
similarity index 100%
rename from dapp/src/theme/Form.ts
rename to dapp/src/theme/formTheme.ts
diff --git a/dapp/src/theme/Heading.ts b/dapp/src/theme/headingTheme.ts
similarity index 100%
rename from dapp/src/theme/Heading.ts
rename to dapp/src/theme/headingTheme.ts
diff --git a/dapp/src/theme/index.ts b/dapp/src/theme/index.ts
index aaf6f4765..eb1c2cd41 100644
--- a/dapp/src/theme/index.ts
+++ b/dapp/src/theme/index.ts
@@ -1,5 +1,4 @@
import { extendTheme } from "@chakra-ui/react"
-import { buttonTheme } from "./Button"
import {
colors,
fonts,
@@ -8,28 +7,27 @@ import {
styles,
zIndices,
} from "./utils"
-import { drawerTheme } from "./Drawer"
-import { modalTheme } from "./Modal"
-import { cardTheme } from "./Card"
-import { tooltipTheme } from "./Tooltip"
-import { headingTheme } from "./Heading"
-import { sidebarTheme } from "./Sidebar"
-import { currencyBalanceTheme } from "./CurrencyBalance"
-import { tokenBalanceInputTheme } from "./TokenBalanceInput"
-import { inputTheme } from "./Input"
-import { alertTheme } from "./Alert"
-import { formTheme } from "./Form"
-import { formLabelTheme } from "./FormLabel"
-import { formErrorTheme } from "./FormError"
-import { tagTheme } from "./Tag"
-import { spinnerTheme } from "./Spinner"
-import { linkTheme } from "./Link"
-import { skeletonTheme } from "./Skeleton"
-import { closeButtonTheme } from "./CloseButton"
-import { progressTheme } from "./Progress"
-import { countdownTheme } from "./Countdown"
-import { footerTheme } from "./Footer"
-import { acreTVLProgressTheme } from "./AcreTVLProgress"
+import { acreTVLProgressTheme } from "./acreTVLProgressTheme"
+import { alertTheme } from "./alertTheme"
+import { buttonTheme } from "./buttonTheme"
+import { cardTheme } from "./cardTheme"
+import { closeButtonTheme } from "./closeButtonTheme"
+import { countdownTheme } from "./countdownTheme"
+import { currencyBalanceTheme } from "./currencyBalanceTheme"
+import { footerTheme } from "./footerTheme"
+import { formErrorTheme } from "./formErrorTheme"
+import { formLabelTheme } from "./formLabelTheme"
+import { formTheme } from "./formTheme"
+import { headingTheme } from "./headingTheme"
+import { inputTheme } from "./inputTheme"
+import { linkTheme } from "./linkTheme"
+import { modalTheme } from "./modalTheme"
+import { progressTheme } from "./progressTheme"
+import { skeletonTheme } from "./skeletonTheme"
+import { spinnerTheme } from "./spinnerTheme"
+import { tagTheme } from "./tagTheme"
+import { tokenBalanceInputTheme } from "./tokenBalanceInputTheme"
+import { tooltipTheme } from "./tooltipTheme"
const defaultTheme = {
// TODO: Remove when dark mode is ready
@@ -53,7 +51,6 @@ const defaultTheme = {
Card: cardTheme,
CloseButton: closeButtonTheme,
CurrencyBalance: currencyBalanceTheme,
- Drawer: drawerTheme,
Form: formTheme,
FormLabel: formLabelTheme,
FormError: formErrorTheme,
@@ -61,7 +58,6 @@ const defaultTheme = {
Input: inputTheme,
Link: linkTheme,
Modal: modalTheme,
- Sidebar: sidebarTheme,
Spinner: spinnerTheme,
Tag: tagTheme,
TokenBalanceInput: tokenBalanceInputTheme,
diff --git a/dapp/src/theme/Input.ts b/dapp/src/theme/inputTheme.ts
similarity index 100%
rename from dapp/src/theme/Input.ts
rename to dapp/src/theme/inputTheme.ts
diff --git a/dapp/src/theme/Link.ts b/dapp/src/theme/linkTheme.ts
similarity index 100%
rename from dapp/src/theme/Link.ts
rename to dapp/src/theme/linkTheme.ts
diff --git a/dapp/src/theme/Modal.ts b/dapp/src/theme/modalTheme.ts
similarity index 84%
rename from dapp/src/theme/Modal.ts
rename to dapp/src/theme/modalTheme.ts
index fd79f1bdc..d4cb15d5d 100644
--- a/dapp/src/theme/Modal.ts
+++ b/dapp/src/theme/modalTheme.ts
@@ -2,23 +2,27 @@ import { modalAnatomy as parts } from "@chakra-ui/anatomy"
import { createMultiStyleConfigHelpers, defineStyle } from "@chakra-ui/react"
const baseStyleContainer = defineStyle({
- px: 8,
+ px: { base: 3, sm: 8 },
})
const baseStyleDialog = defineStyle({
- marginTop: { base: 12, sm: "var(--chakra-space-modal_shift)" },
+ marginTop: {
+ base: 12,
+ sm: "9.75rem", // 156px
+ },
marginBottom: 8,
boxShadow: "none",
borderRadius: "xl",
+ p: { base: 5, sm: 0 },
bg: "gold.100",
border: "none",
})
const baseCloseButton = defineStyle({
- top: -7,
- right: -7,
- boxSize: 7,
- rounded: "100%",
+ top: { base: 3, sm: -7 },
+ right: { base: 3, sm: -7 },
+ boxSize: { sm: 7 },
+ rounded: { sm: "100%" },
bg: "opacity.white.5",
_hover: {
@@ -37,8 +41,8 @@ const baseStyleHeader = defineStyle({
fontSize: "xl",
lineHeight: "xl",
fontWeight: "bold",
- pt: 10,
- px: 10,
+ pt: { sm: 8 },
+ px: { sm: 8 },
pb: 8,
})
@@ -50,15 +54,15 @@ const baseStyleBody = defineStyle({
alignItems: "center",
gap: 6,
pt: 0,
- px: 8,
- pb: 10,
+ px: { base: 0, sm: 8 },
+ pb: { base: 0, sm: 8 },
})
const baseStyleFooter = defineStyle({
flexDirection: "column",
gap: 6,
- px: 8,
- pb: 10,
+ px: { base: 0, sm: 8 },
+ pb: { base: 0, sm: 8 },
})
const multiStyleConfig = createMultiStyleConfigHelpers(parts.keys)
diff --git a/dapp/src/theme/Progress.ts b/dapp/src/theme/progressTheme.ts
similarity index 100%
rename from dapp/src/theme/Progress.ts
rename to dapp/src/theme/progressTheme.ts
diff --git a/dapp/src/theme/Skeleton.ts b/dapp/src/theme/skeletonTheme.ts
similarity index 100%
rename from dapp/src/theme/Skeleton.ts
rename to dapp/src/theme/skeletonTheme.ts
diff --git a/dapp/src/theme/Spinner.ts b/dapp/src/theme/spinnerTheme.ts
similarity index 100%
rename from dapp/src/theme/Spinner.ts
rename to dapp/src/theme/spinnerTheme.ts
diff --git a/dapp/src/theme/Tag.ts b/dapp/src/theme/tagTheme.ts
similarity index 100%
rename from dapp/src/theme/Tag.ts
rename to dapp/src/theme/tagTheme.ts
diff --git a/dapp/src/theme/TokenBalanceInput.ts b/dapp/src/theme/tokenBalanceInputTheme.ts
similarity index 100%
rename from dapp/src/theme/TokenBalanceInput.ts
rename to dapp/src/theme/tokenBalanceInputTheme.ts
diff --git a/dapp/src/theme/Tooltip.ts b/dapp/src/theme/tooltipTheme.ts
similarity index 100%
rename from dapp/src/theme/Tooltip.ts
rename to dapp/src/theme/tooltipTheme.ts
diff --git a/dapp/src/theme/utils/semanticTokens.ts b/dapp/src/theme/utils/semanticTokens.ts
index 8ad1298ba..8fb5ba562 100644
--- a/dapp/src/theme/utils/semanticTokens.ts
+++ b/dapp/src/theme/utils/semanticTokens.ts
@@ -1,11 +1,5 @@
export const semanticTokens = {
space: {
- header_height: 28,
- header_height_xl: 36,
- modal_shift: "9.75rem", // 156px
dashboard_card_padding: 5,
},
- sizes: {
- sidebar_width: 80,
- },
}
diff --git a/dapp/src/theme/utils/zIndices.ts b/dapp/src/theme/utils/zIndices.ts
index 3e9fb1a4b..3ada56278 100644
--- a/dapp/src/theme/utils/zIndices.ts
+++ b/dapp/src/theme/utils/zIndices.ts
@@ -1,9 +1,6 @@
export const zIndices = {
- sidebar: 1450,
- drawer: 1470,
mobileBanner: 1500,
header: 1400,
footer: 1380,
- modalContent: 1410,
modalOverlay: 1390,
}
diff --git a/dapp/src/types/core.ts b/dapp/src/types/core.ts
index 2fce09a01..9dcdb75b8 100644
--- a/dapp/src/types/core.ts
+++ b/dapp/src/types/core.ts
@@ -1,5 +1 @@
-export type Tuple = [T, T]
-
-export type WithRequired = T & { [P in K]-?: T[P] }
-
export type Optional = Pick, K> & Omit
diff --git a/dapp/src/types/error.ts b/dapp/src/types/error.ts
deleted file mode 100644
index 975af0f65..000000000
--- a/dapp/src/types/error.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export type ConnectionErrorData = {
- title: string
- description: string
-}
diff --git a/dapp/src/types/index.ts b/dapp/src/types/index.ts
index 13bdda39c..7b39c9288 100644
--- a/dapp/src/types/index.ts
+++ b/dapp/src/types/index.ts
@@ -10,8 +10,6 @@ export * from "./fee"
export * from "./modal"
export * from "./form"
export * from "./eip1193"
-export * from "./error"
export * from "./status"
export * from "./orangekit"
-export * from "./ledgerLive"
export * from "./dapp-mode"
diff --git a/dapp/src/types/ledgerLive.ts b/dapp/src/types/ledgerLive.ts
deleted file mode 100644
index 6cd4aea65..000000000
--- a/dapp/src/types/ledgerLive.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export type LedgerLiveError = {
- message?: string
- name?: string
- stack?: string
-}
diff --git a/dapp/src/utils/acreApi.ts b/dapp/src/utils/acreApi.ts
index 73a93b8a1..b67e3d918 100644
--- a/dapp/src/utils/acreApi.ts
+++ b/dapp/src/utils/acreApi.ts
@@ -45,9 +45,9 @@ const getPointsData = async () => {
const response = await axios.get(url)
return {
- dropAt: response.data.dropAt,
+ nextDropTimestamp: response.data.dropAt,
isCalculationInProgress: response.data.isCalculationInProgress,
- totalPool: Number(response.data.totalPool),
+ totalPoolBalance: Number(response.data.totalPool),
}
}
@@ -62,8 +62,8 @@ const getPointsDataByUser = async (address: string) => {
const response = await axios.get(url)
return {
- claimed: Number(response.data.claimed),
- unclaimed: Number(response.data.unclaimed),
+ totalBalance: Number(response.data.claimed),
+ claimableBalance: Number(response.data.unclaimed),
isEligible: response.data.isEligible,
}
}
diff --git a/dapp/src/utils/activities.ts b/dapp/src/utils/activities.ts
index 7349a985b..8d599f64b 100644
--- a/dapp/src/utils/activities.ts
+++ b/dapp/src/utils/activities.ts
@@ -9,6 +9,11 @@ export const isActivityCompleted = (activity: Activity): boolean =>
export const getActivityTimestamp = (activity: Activity): number =>
activity?.finalizedAt ?? activity.initializedAt
+export const hasPendingDeposits = (activities: Activity[]): boolean =>
+ activities.some(
+ (activity) => activity.status === "pending" && activity.type === "deposit",
+ )
+
export const sortActivitiesByTimestamp = (activities: Activity[]): Activity[] =>
[...activities].sort(
(activity1, activity2) =>
@@ -21,12 +26,31 @@ export function getEstimatedDuration(
amount: bigint,
type: ActivityType,
): string {
+ // Withdrawal duration is related to the tBTC redemption process, which takes
+ // approximately 5 - 7 hours. We use the average value of 6 hours.
if (isWithdrawType(type)) return "6 hours"
- if (amount < MIN_LIMIT_VALUE_DURATION) return "1 hour"
-
- if (amount >= MIN_LIMIT_VALUE_DURATION && amount < MAX_LIMIT_VALUE_DURATION)
- return "2 hours"
-
+ // Deposit duration is related to the tBTC minting process, which varies based
+ // on the amount of BTC deposited.
+ // Each threshold requires a different number of Bitcoin transaction confirmations:
+ // <0.1 BTC: 1 Bitcoin block confirmation (~10 minutes),
+ // >=0.1 BTC and <1 BTC: 3 Bitcoin block confirmations (~30 minutes),
+ // >=1 BTC: 6 Bitcoin block confirmations (~60 minutes).
+ // The duration of the transaction minting process depends on the Bitcoin network
+ // congestion, and the fee paid by the user.
+ //
+ // After the required number of Bitcoin block confirmations, the tBTC optimistic
+ // minting process starts. The optimistic minting process takes approximately
+ // 1 hour to complete.
+ // After optimistic minting is completed, the Acre bots will finalize the deposit
+ // in no more than 10 minutes.
+ //
+ // We round the estimated duration up to the nearest hour.
+ //
+ // For <0.1 BTC estimated duration is around 1 hour 20 minutes.
+ if (amount < MIN_LIMIT_VALUE_DURATION) return "2 hours"
+ // For <1 BTC estimated duration is around 1 hours 40 minutes.
+ if (amount < MAX_LIMIT_VALUE_DURATION) return "2 hours"
+ // For >=1 BTC estimated duration is around 2 hours 10 minutes.
return "3 hours"
}
diff --git a/dapp/src/utils/index.ts b/dapp/src/utils/index.ts
index c79d332b7..64e1ffc9b 100644
--- a/dapp/src/utils/index.ts
+++ b/dapp/src/utils/index.ts
@@ -3,7 +3,6 @@ export * from "./address"
export * from "./forms"
export * from "./currency"
export * from "./chain"
-export * from "./text"
export * from "./time"
export * from "./promise"
export * from "./verifyDepositAddress"
diff --git a/dapp/src/utils/orangekit/index.ts b/dapp/src/utils/orangekit/index.ts
index 18f355df4..f900de0c9 100644
--- a/dapp/src/utils/orangekit/index.ts
+++ b/dapp/src/utils/orangekit/index.ts
@@ -1,19 +1,12 @@
-import {
- ACRE_SESSION_EXPIRATION_TIME,
- CONNECTION_ERRORS,
- wallets,
-} from "#/constants"
-import {
- ConnectionErrorData,
- OrangeKitError,
- OrangeKitConnector,
-} from "#/types"
+import { ACRE_SESSION_EXPIRATION_TIME, wallets } from "#/constants"
+import { OrangeKitError, OrangeKitConnector } from "#/types"
import {
isUnsupportedBitcoinAddressError,
isWalletNetworkDoesNotMatchProviderChainError,
} from "@orangekit/react"
import { Connector } from "wagmi"
import { SignInWithWalletMessage } from "@orangekit/sign-in-with-wallet"
+import { ConnectionAlert } from "#/components/ConnectWalletModal/ConnectWalletAlert"
import { getExpirationDate } from "../time"
import { getOrangeKitLedgerLiveConnector } from "./ledger-live"
@@ -71,22 +64,22 @@ const typeConversionToConnector = (connector?: OrangeKitConnector): Connector =>
const parseOrangeKitConnectionError = (
error: OrangeKitError,
-): ConnectionErrorData => {
+): ConnectionAlert => {
const { cause } = error
if (isWalletConnectionRejectedError(cause)) {
- return CONNECTION_ERRORS.REJECTED
+ return ConnectionAlert.Rejected
}
if (isUnsupportedBitcoinAddressError(cause)) {
- return CONNECTION_ERRORS.NOT_SUPPORTED
+ return ConnectionAlert.NotSupported
}
if (isWalletNetworkDoesNotMatchProviderChainError(cause)) {
- return CONNECTION_ERRORS.NETWORK_MISMATCH
+ return ConnectionAlert.NetworkMismatch
}
- return CONNECTION_ERRORS.DEFAULT
+ return ConnectionAlert.Default
}
async function verifySignInWithWalletMessage(
@@ -107,6 +100,33 @@ async function verifySignInWithWalletMessage(
return result.data
}
+/**
+ * Finds the extended public key (xpub) of the user's account from URL. Users
+ * can be redirected to the exact app in the Ledger Live application. One of the
+ * parameters passed via URL is `accountId` - the ID of the user's account in
+ * Ledger Live.
+ * @see https://developers.ledger.com/docs/ledger-live/exchange/earn/liveapp#url-parameters-for-direct-navigation
+ *
+ * @param {string} url Request url
+ * @returns The extended public key (xpub) of the user's account if the search
+ * parameter `accountId` exists in the URL. Otherwise `undefined`.
+ */
+function findXpubFromUrl(url: string): string | undefined {
+ const parsedUrl = new URL(url)
+
+ const accountId = parsedUrl.searchParams.get("accountId")
+
+ if (!accountId) return undefined
+
+ // The fourth value separated by `:` is extended public key. See the
+ // account ID template: `js:2:bitcoin_testnet::`.
+ const xpubFromAccountId = accountId.split(":")[3]
+
+ if (!xpubFromAccountId) return undefined
+
+ return xpubFromAccountId
+}
+
export default {
getWalletInfo,
isWalletInstalled,
@@ -119,4 +139,5 @@ export default {
isWalletConnectionRejectedError,
verifySignInWithWalletMessage,
getOrangeKitLedgerLiveConnector,
+ findXpubFromUrl,
}
diff --git a/dapp/src/utils/orangekit/ledger-live/bitcoin-provider.ts b/dapp/src/utils/orangekit/ledger-live/bitcoin-provider.ts
index 7dab9c5ad..316e628a4 100644
--- a/dapp/src/utils/orangekit/ledger-live/bitcoin-provider.ts
+++ b/dapp/src/utils/orangekit/ledger-live/bitcoin-provider.ts
@@ -58,9 +58,12 @@ function numberToValidHexString(value: number): string {
return `0x${hex}`
}
-export type AcreLedgerLiveBitcoinProviderOptions = {
- tryConnectToAddress: string | undefined
-}
+export type AcreLedgerLiveBitcoinProviderOptions =
+ | {
+ tryConnectToAddress?: string
+ tryConnectToAccountByXpub?: never
+ }
+ | { tryConnectToAddress?: never; tryConnectToAccountByXpub?: string }
/**
* Ledger Live Wallet API Bitcoin Provider.
@@ -90,6 +93,7 @@ export default class AcreLedgerLiveBitcoinProvider
network: BitcoinNetwork,
options: AcreLedgerLiveBitcoinProviderOptions = {
tryConnectToAddress: undefined,
+ tryConnectToAccountByXpub: undefined,
},
) {
const windowMessageTransport = new WindowMessageTransport()
@@ -115,6 +119,7 @@ export default class AcreLedgerLiveBitcoinProvider
walletApiClient: WalletAPIClient,
options: AcreLedgerLiveBitcoinProviderOptions = {
tryConnectToAddress: undefined,
+ tryConnectToAccountByXpub: undefined,
},
) {
this.#network = network
@@ -140,12 +145,24 @@ export default class AcreLedgerLiveBitcoinProvider
currencyIds,
})
- if (this.#options.tryConnectToAddress) {
+ if (
+ this.#options.tryConnectToAddress ||
+ this.#options.tryConnectToAccountByXpub
+ ) {
for (let i = 0; i < accounts.length; i += 1) {
const acc = accounts[i]
+ if (
+ this.#options.tryConnectToAccountByXpub &&
+ // eslint-disable-next-line no-await-in-loop
+ (await this.#walletApiClient.bitcoin.getXPub(acc.id)) ===
+ this.#options.tryConnectToAccountByXpub
+ ) {
+ this.#account = acc
+ break
+ }
+
// eslint-disable-next-line no-await-in-loop
const address = await this.#getAddress(acc.id)
-
if (address === this.#options.tryConnectToAddress) {
this.#account = acc
break
diff --git a/dapp/src/utils/referralProgram.ts b/dapp/src/utils/referralProgram.ts
index 36f48b9a3..82a3bfbc5 100644
--- a/dapp/src/utils/referralProgram.ts
+++ b/dapp/src/utils/referralProgram.ts
@@ -9,9 +9,23 @@ const EMBEDDED_APP_TO_REFERRAL: Record = {
"ledger-live": 2083,
}
-const isValidReferral = (value: number) => {
- const isInteger = Number.isInteger(value)
- return isInteger && value >= 0 && value <= MAX_UINT16
+const isValidReferral = (value: unknown) => {
+ let valueAsNumber: number | undefined
+
+ if (typeof value === "string") {
+ // Only digits w/o leading zeros.
+ const isNumber = /^(?:[1-9][0-9]*|0)$/.test(value)
+ valueAsNumber = isNumber ? Number(value) : undefined
+ } else if (typeof value === "number") {
+ valueAsNumber = value
+ }
+
+ return (
+ !!valueAsNumber &&
+ Number.isInteger(valueAsNumber) &&
+ valueAsNumber >= 0 &&
+ valueAsNumber <= MAX_UINT16
+ )
}
const getReferralFromURL = () =>
diff --git a/dapp/src/utils/router.ts b/dapp/src/utils/router.ts
index 868ab3a0c..059f2872a 100644
--- a/dapp/src/utils/router.ts
+++ b/dapp/src/utils/router.ts
@@ -1,7 +1,4 @@
-import { To, redirect } from "react-router-dom"
-import { isString } from "./type-check"
-
-const getURLPath = (to: To) => (isString(to) ? to : to.pathname)
+import { redirect } from "react-router-dom"
const getURLParamFromHref = (href: string, paramName: string) => {
const { searchParams } = new URL(href)
@@ -19,7 +16,6 @@ const redirectWithSearchParams = (url: string, to: string) => {
}
export default {
- getURLPath,
getURLParam,
getURLParamFromHref,
redirectWithSearchParams,
diff --git a/dapp/src/utils/text.ts b/dapp/src/utils/text.ts
deleted file mode 100644
index 36e034408..000000000
--- a/dapp/src/utils/text.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export const capitalizeFirstLetter = (text: string): string =>
- text[0].toUpperCase() + text.slice(1)
diff --git a/dapp/src/vite-env.d.ts b/dapp/src/vite-env.d.ts
index 564bf6a80..eb8de6913 100644
--- a/dapp/src/vite-env.d.ts
+++ b/dapp/src/vite-env.d.ts
@@ -7,16 +7,18 @@ interface ImportMetaEnv {
readonly VITE_REFERRAL: number
readonly VITE_TBTC_API_ENDPOINT: string
readonly VITE_GELATO_RELAY_API_KEY: string
- readonly VITE_FEATURE_FLAG_GAMIFICATION_ENABLED: string
readonly VITE_FEATURE_FLAG_WITHDRAWALS_ENABLED: string
readonly VITE_FEATURE_FLAG_OKX_WALLET_ENABLED: string
readonly VITE_FEATURE_FLAG_XVERSE_WALLET_ENABLED: string
readonly VITE_FEATURE_FLAG_ACRE_POINTS_ENABLED: string
readonly VITE_FEATURE_FLAG_TVL_ENABLED: string
readonly VITE_FEATURE_GATING_DAPP_ENABLED: string
+ readonly VITE_FEATURE_POSTHOG_ENABLED: string
readonly VITE_SUBGRAPH_API_KEY: string
readonly VITE_LATEST_COMMIT_HASH: string
readonly VITE_ACRE_API_ENDPOINT: string
+ readonly VITE_POSTHOG_API_HOST: string
+ readonly VITE_POSTHOG_API_KEY: string
readonly VITE_FEATURE_MOBILE_MODE_ENABLED: string
}
diff --git a/dapp/src/wagmiConfig.ts b/dapp/src/wagmiConfig.ts
index 19d1cf4e4..972a38fda 100644
--- a/dapp/src/wagmiConfig.ts
+++ b/dapp/src/wagmiConfig.ts
@@ -5,6 +5,7 @@ import { env } from "./constants"
import { getLastUsedBtcAddress } from "./hooks/useLastUsedBtcAddress"
import referralProgram, { EmbedApp } from "./utils/referralProgram"
import { orangeKit } from "./utils"
+import { AcreLedgerLiveBitcoinProviderOptions } from "./utils/orangekit/ledger-live/bitcoin-provider"
const isTestnet = env.USE_TESTNET
const CHAIN_ID = isTestnet ? sepolia.id : mainnet.id
@@ -34,12 +35,19 @@ async function getWagmiConfig() {
let createEmbedConnectorFn
const embeddedApp = referralProgram.getEmbeddedApp()
if (referralProgram.isEmbedApp(embeddedApp)) {
+ const lastUsedBtcAddress = getLastUsedBtcAddress()
+ const xpub = orangeKit.findXpubFromUrl(window.location.href)
+ const ledgerLiveConnectorOptions: AcreLedgerLiveBitcoinProviderOptions =
+ xpub
+ ? { tryConnectToAccountByXpub: xpub }
+ : {
+ tryConnectToAddress: lastUsedBtcAddress,
+ }
+
const orangeKitLedgerLiveConnector =
orangeKit.getOrangeKitLedgerLiveConnector({
...connectorConfig,
- options: {
- tryConnectToAddress: getLastUsedBtcAddress(),
- },
+ options: ledgerLiveConnectorOptions,
})
const embedConnectorsMap: Record<
diff --git a/dapp/test/sentry.test.ts b/dapp/test/sentry.test.ts
new file mode 100644
index 000000000..cb7e9d896
--- /dev/null
+++ b/dapp/test/sentry.test.ts
@@ -0,0 +1,38 @@
+import { describe, expect, it, vi } from "vitest"
+import * as Sentry from "@sentry/react"
+import sentry from "#/sentry"
+
+describe("sentry", () => {
+ describe("setUser", () => {
+ vi.mock("@sentry/react")
+
+ const testCases = [
+ { bitcoinAddress: undefined, expectedResult: null },
+ { bitcoinAddress: "", expectedResult: null },
+ { bitcoinAddress: " ", expectedResult: null },
+ {
+ bitcoinAddress: "17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem",
+ expectedResult: { id: "1f520a9757" },
+ },
+ {
+ bitcoinAddress: "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
+ expectedResult: { id: "6cd42dab02" },
+ },
+ {
+ bitcoinAddress: "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4",
+ expectedResult: { id: "6cd42dab02" },
+ },
+ ]
+
+ describe.each(testCases)(
+ "when address is $bitcoinAddress",
+ ({ bitcoinAddress, expectedResult }) => {
+ it("should set expected user in Sentry", () => {
+ sentry.setUser(bitcoinAddress)
+
+ expect(Sentry.setUser).toHaveBeenCalledWith(expectedResult)
+ })
+ },
+ )
+ })
+})
diff --git a/dapp/test/utils/activities.test.ts b/dapp/test/utils/activities.test.ts
new file mode 100644
index 000000000..c25c4f93a
--- /dev/null
+++ b/dapp/test/utils/activities.test.ts
@@ -0,0 +1,52 @@
+import { describe, expect, it } from "vitest"
+import { getEstimatedDuration } from "#/utils/activities"
+
+describe("Utils functions for activities", () => {
+ describe("getEstimatedDuration", () => {
+ describe("withdraw", () => {
+ describe.each([
+ // 0.01 BTC
+ { value: 0.01, expectedResult: "6 hours" },
+ // 0.1 BTC
+ { value: 0.1, expectedResult: "6 hours" },
+ // 1 BTC
+ { value: 1, expectedResult: "6 hours" },
+ // 10 BTC
+ { value: 10, expectedResult: "6 hours" },
+ ])("when it is $value BTC", ({ value, expectedResult }) => {
+ it(`should return ${expectedResult}`, () => {
+ expect(getEstimatedDuration(BigInt(value * 1e8), "withdraw")).toEqual(
+ expectedResult,
+ )
+ })
+ })
+ })
+
+ describe("deposit", () => {
+ describe.each([
+ // 0.0001 BTC
+ { value: 0.0001, expectedResult: "2 hours" },
+ // 0.001 BTC
+ { value: 0.001, expectedResult: "2 hours" },
+ // 0.01 BTC
+ { value: 0.01, expectedResult: "2 hours" },
+ // 0.09 BTC
+ { value: 0.09, expectedResult: "2 hours" },
+ // 0.1 BTC
+ { value: 0.1, expectedResult: "2 hours" },
+ // 0.9 BTC
+ { value: 0.9, expectedResult: "2 hours" },
+ // 1 BTC
+ { value: 1, expectedResult: "3 hours" },
+ // 10 BTC
+ { value: 10, expectedResult: "3 hours" },
+ ])("when it is $value BTC", ({ value, expectedResult }) => {
+ it(`should return ${expectedResult}`, () => {
+ expect(getEstimatedDuration(BigInt(value * 1e8), "deposit")).toEqual(
+ expectedResult,
+ )
+ })
+ })
+ })
+ })
+})
diff --git a/dapp/src/utils/tests/numbers.test.ts b/dapp/test/utils/numbers.test.ts
similarity index 96%
rename from dapp/src/utils/tests/numbers.test.ts
rename to dapp/test/utils/numbers.test.ts
index 6151a888a..1699ee0d7 100644
--- a/dapp/src/utils/tests/numbers.test.ts
+++ b/dapp/test/utils/numbers.test.ts
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest"
-import { roundUp } from "../numbers"
+import { roundUp } from "#/utils/numbers"
describe("Utils functions for numbers", () => {
describe("roundUp", () => {
diff --git a/dapp/src/utils/orangekit/ledger-live/tests/bitcoin-provider.test.ts b/dapp/test/utils/orangekit/ledger-live/bitcoin-provider.test.ts
similarity index 99%
rename from dapp/src/utils/orangekit/ledger-live/tests/bitcoin-provider.test.ts
rename to dapp/test/utils/orangekit/ledger-live/bitcoin-provider.test.ts
index 58d1d0f89..36ed0375e 100644
--- a/dapp/src/utils/orangekit/ledger-live/tests/bitcoin-provider.test.ts
+++ b/dapp/test/utils/orangekit/ledger-live/bitcoin-provider.test.ts
@@ -8,7 +8,7 @@ import { Balance } from "@orangekit/react/dist/src/wallet/bitcoin-wallet-provide
import { AcreMessageType } from "@ledgerhq/wallet-api-acre-module"
import { ZeroAddress } from "ethers"
import BigNumber from "bignumber.js"
-import AcreLedgerLiveBitcoinProvider from "../bitcoin-provider"
+import AcreLedgerLiveBitcoinProvider from "#/utils/orangekit/ledger-live/bitcoin-provider"
describe("AcreLedgerLiveBitcoinProvider", () => {
const bitcoinAddress = "mjc2zGWypwpNyDi4ZxGbBNnUA84bfgiwYc"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index cf2c53ac6..b967a0030 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -39,11 +39,11 @@ importers:
specifier: 1.6.0
version: 1.6.0
'@orangekit/react':
- specifier: 1.0.0-beta.33
- version: 1.0.0-beta.33(@react-native-async-storage/async-storage@1.23.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10)))(@tanstack/query-core@5.45.0)(@tanstack/react-query@5.45.0(react@18.3.1))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10))(rollup@4.18.0)(typescript@5.4.5)(utf-8-validate@5.0.10)(zod@3.23.8)
+ specifier: 1.0.0-beta.34
+ version: 1.0.0-beta.34(@react-native-async-storage/async-storage@1.23.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10)))(@tanstack/query-core@5.45.0)(@tanstack/react-query@5.45.0(react@18.3.1))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10))(rollup@4.18.0)(typescript@5.4.5)(utf-8-validate@5.0.10)(zod@3.23.8)
'@orangekit/sign-in-with-wallet':
- specifier: 1.0.0-beta.6
- version: 1.0.0-beta.6(bech32@2.0.0)(ethers@6.13.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))
+ specifier: 1.0.0-beta.7
+ version: 1.0.0-beta.7(bech32@2.0.0)(ethers@6.13.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))
'@reduxjs/toolkit':
specifier: ^2.2.0
version: 2.2.5(react-redux@9.1.2(@types/react@18.3.3)(react@18.3.1)(redux@5.0.1))(react@18.3.1)
@@ -89,6 +89,9 @@ importers:
mustache:
specifier: ^4.2.0
version: 4.2.0
+ posthog-js:
+ specifier: ^1.186.1
+ version: 1.186.1
react:
specifier: ^18.2.0
version: 18.3.1
@@ -172,8 +175,8 @@ importers:
specifier: 2.5.0-dev.3
version: 2.5.0-dev.3(@keep-network/keep-core@1.8.1-dev.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)
'@orangekit/sdk':
- specifier: 1.0.0-beta.18
- version: 1.0.0-beta.18(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)
+ specifier: 1.0.0-beta.19
+ version: 1.0.0-beta.19(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)
ethers:
specifier: 6.10.0
version: 6.10.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
@@ -2692,17 +2695,17 @@ packages:
'@orangekit/contracts@1.0.0-beta.3':
resolution: {integrity: sha512-xP1Oz/JzuHypg5DcsHayINhFSL5M/tCmRP/stmNAvjeebXhrKuALKAHWn98H0Mo3QfYYrz8UcltQPeH6+68n6A==}
- '@orangekit/react@1.0.0-beta.33':
- resolution: {integrity: sha512-b5VMFB2tZad86FNdtylwsKaXlXlrSjY9cIMsaJMUh7EEWEoVvtndbCsHrISKbspm7y+S5nNmGlE3WcQvR8RWWw==}
+ '@orangekit/react@1.0.0-beta.34':
+ resolution: {integrity: sha512-/PTfYcu/BU4ssltIuy+AKqbaGzue+MEJHwd7+4aZhXhwJ+/xrYo3mJ1tSwj6K8lAJCl/N25vq7zNJgVgjLK1WQ==}
- '@orangekit/sdk@1.0.0-beta.18':
- resolution: {integrity: sha512-031JkI0F8gdGzj/7sKbJ52k0Xj/E4uwp/O0SWFDCXyZC0vwtjlw3ItAaZz+nIj820b6dSM6pjL+P7um3m9AK3Q==}
+ '@orangekit/sdk@1.0.0-beta.19':
+ resolution: {integrity: sha512-sIqzu3QTb0WkB4Ir4zHX4tP7hf+uZBXX60q/ww6UzwPjoqGL3dOdui59lTZSNygdMbydLE3y9FSNZPNkQvm+gA==}
- '@orangekit/sign-in-with-wallet-parser@1.0.0-beta.6':
- resolution: {integrity: sha512-Yi6ohSJV4/Ovrq5c7jD+kPE8pZxLhWtFbZjKRwUW8JL60P/tcyT5o0etul0reqcY2iBlIo5aoC2Hh0noRGl86w==}
+ '@orangekit/sign-in-with-wallet-parser@1.0.0-beta.7':
+ resolution: {integrity: sha512-C7gmliw2TJOQzIMpuhDZhIGlp7zFSqNsa/oevNaqnB+i0zFIsXZjsKg2oddr8smYG1+KhbX+V+O3AeaPCSsK9A==}
- '@orangekit/sign-in-with-wallet@1.0.0-beta.6':
- resolution: {integrity: sha512-UzNG8QHehem2wayWDyMBd13z1lQUBKsSq+NjGK6KHYWUbet6EizBeVZK7jSruzTHba3gDI8I3NH071E6sxTFtQ==}
+ '@orangekit/sign-in-with-wallet@1.0.0-beta.7':
+ resolution: {integrity: sha512-o2U5oi8b+2cc0Lel4p7myp1pQ6J2OLCdVTRhYQ4Te4+iqVFns1UlHx28pEo0zvGCJ5CwHQBmJHX6QguL02Kj4Q==}
peerDependencies:
bech32: '=2.0.0'
ethers: ^6.0.8
@@ -3113,8 +3116,13 @@ packages:
peerDependencies:
ethers: 5.4.0
+ '@safe-global/safe-core-sdk-types@4.1.1':
+ resolution: {integrity: sha512-5NIWG7OjV+C5iquux0yPcu8SHUzg1qJXJ/jAQcPwXGTC7ZVsFawnR43/l2Vg9zEwf0RF0xTm3W8DXkaBYORiEQ==}
+ deprecated: 'WARNING: This project has been renamed to @safe-global/types-kit. Please, migrate from @safe-global/safe-core-sdk-types@5.1.0 to @safe-global/types-kit@1.0.0.'
+
'@safe-global/safe-core-sdk-types@5.0.1':
resolution: {integrity: sha512-xIlHZ9kaAIwEhR0OY0i2scdcQyrc0tDJ+eZZ04lhvg81cgYLY1Z5wfJQqazR2plPT1Hz0A9C79jYdUVvzoF/tw==}
+ deprecated: 'WARNING: This project has been renamed to @safe-global/types-kit. Please, migrate from @safe-global/safe-core-sdk-types@5.1.0 to @safe-global/types-kit@1.0.0.'
'@safe-global/safe-deployments@1.37.0':
resolution: {integrity: sha512-OInLNWC9EPem/eOsvPdlq4Gt/08Nfhslm9z6T92Jvjmcu6hs85vjfnDP1NrzwcOmsCarATU5NH2bTITd9VNCPw==}
@@ -5063,6 +5071,9 @@ packages:
core-js-compat@3.37.1:
resolution: {integrity: sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==}
+ core-js@3.39.0:
+ resolution: {integrity: sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==}
+
core-util-is@1.0.2:
resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
@@ -6068,6 +6079,9 @@ packages:
fd-slicer@1.1.0:
resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
+ fflate@0.4.8:
+ resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==}
+
file-entry-cache@6.0.1:
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
engines: {node: ^10.12.0 || >=12.0.0}
@@ -8616,6 +8630,9 @@ packages:
resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
engines: {node: ^10 || ^12 || >=14}
+ posthog-js@1.186.1:
+ resolution: {integrity: sha512-m6TNW01nfqErwMxaZxNScYdMaUJO0s3bbmt/tboL29yZDnuHdOiYFbG+T4MCxdFxjWRa5gOR25bQD/SSt1t/4A==}
+
preact@10.22.0:
resolution: {integrity: sha512-RRurnSjJPj4rp5K6XoP45Ui33ncb7e4H7WiOHVpjbkvqvA3U+N8Z6Qbo0AE6leGYBV66n8EhEaFixvIu3SkxFw==}
@@ -10486,6 +10503,9 @@ packages:
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
engines: {node: '>= 8'}
+ web-vitals@4.2.4:
+ resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==}
+
web3-bzz@1.10.4:
resolution: {integrity: sha512-ZZ/X4sJ0Uh2teU9lAGNS8EjveEppoHNQiKlOXAjedsrdWuaMErBPdLQjXfcrYvN6WM6Su9PMsAxf3FXXZ+HwQw==}
engines: {node: '>=8.0.0'}
@@ -14831,9 +14851,9 @@ snapshots:
transitivePeerDependencies:
- ethers
- '@orangekit/react@1.0.0-beta.33(@react-native-async-storage/async-storage@1.23.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10)))(@tanstack/query-core@5.45.0)(@tanstack/react-query@5.45.0(react@18.3.1))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10))(rollup@4.18.0)(typescript@5.4.5)(utf-8-validate@5.0.10)(zod@3.23.8)':
+ '@orangekit/react@1.0.0-beta.34(@react-native-async-storage/async-storage@1.23.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10)))(@tanstack/query-core@5.45.0)(@tanstack/react-query@5.45.0(react@18.3.1))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10))(rollup@4.18.0)(typescript@5.4.5)(utf-8-validate@5.0.10)(zod@3.23.8)':
dependencies:
- '@orangekit/sdk': 1.0.0-beta.18(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)
+ '@orangekit/sdk': 1.0.0-beta.19(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)
'@rainbow-me/rainbowkit': 2.0.2(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(viem@2.8.16(bufferutil@4.0.8)(typescript@5.4.5)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.5.12(@react-native-async-storage/async-storage@1.23.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10)))(@tanstack/query-core@5.45.0)(@tanstack/react-query@5.45.0(react@18.3.1))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@4.18.0)(typescript@5.4.5)(utf-8-validate@5.0.10)(viem@2.8.16(bufferutil@4.0.8)(typescript@5.4.5)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8))
ethers: 6.12.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)
react: 18.3.1
@@ -14870,13 +14890,14 @@ snapshots:
- utf-8-validate
- zod
- '@orangekit/sdk@1.0.0-beta.18(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)':
+ '@orangekit/sdk@1.0.0-beta.19(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)':
dependencies:
'@gelatonetwork/relay-sdk': 5.5.6(bufferutil@4.0.8)(utf-8-validate@5.0.10)
'@noble/curves': 1.4.0
'@noble/hashes': 1.4.0
'@orangekit/contracts': 1.0.0-beta.3(ethers@6.12.1(bufferutil@4.0.8)(utf-8-validate@5.0.10))
'@safe-global/protocol-kit': 3.1.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)
+ '@safe-global/safe-core-sdk-types': 4.1.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)
bitcoinjs-lib: 6.1.5
ethers: 6.12.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)
transitivePeerDependencies:
@@ -14886,16 +14907,16 @@ snapshots:
- supports-color
- utf-8-validate
- '@orangekit/sign-in-with-wallet-parser@1.0.0-beta.6':
+ '@orangekit/sign-in-with-wallet-parser@1.0.0-beta.7':
dependencies:
'@noble/hashes': 1.4.0
apg-js: 4.4.0
uri-js: 4.4.1
valid-url: 1.0.9
- '@orangekit/sign-in-with-wallet@1.0.0-beta.6(bech32@2.0.0)(ethers@6.13.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))':
+ '@orangekit/sign-in-with-wallet@1.0.0-beta.7(bech32@2.0.0)(ethers@6.13.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))':
dependencies:
- '@orangekit/sign-in-with-wallet-parser': 1.0.0-beta.6
+ '@orangekit/sign-in-with-wallet-parser': 1.0.0-beta.7
'@stablelib/random': 1.0.2
bech32: 2.0.0
ethers: 6.13.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
@@ -15492,6 +15513,18 @@ snapshots:
dependencies:
ethers: 6.12.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)
+ '@safe-global/safe-core-sdk-types@4.1.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)':
+ dependencies:
+ '@safe-global/safe-deployments': 1.37.0
+ ethers: 6.10.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
+ web3-core: 1.10.4(encoding@0.1.13)
+ web3-utils: 1.10.4
+ transitivePeerDependencies:
+ - bufferutil
+ - encoding
+ - supports-color
+ - utf-8-validate
+
'@safe-global/safe-core-sdk-types@5.0.1(typescript@5.4.5)(zod@3.23.8)':
dependencies:
abitype: 1.0.2(typescript@5.4.5)(zod@3.23.8)
@@ -18323,6 +18356,8 @@ snapshots:
dependencies:
browserslist: 4.23.1
+ core-js@3.39.0: {}
+
core-util-is@1.0.2: {}
core-util-is@1.0.3: {}
@@ -19729,6 +19764,8 @@ snapshots:
dependencies:
pend: 1.2.0
+ fflate@0.4.8: {}
+
file-entry-cache@6.0.1:
dependencies:
flat-cache: 3.2.0
@@ -22902,6 +22939,13 @@ snapshots:
picocolors: 1.0.1
source-map-js: 1.2.0
+ posthog-js@1.186.1:
+ dependencies:
+ core-js: 3.39.0
+ fflate: 0.4.8
+ preact: 10.22.0
+ web-vitals: 4.2.4
+
preact@10.22.0: {}
prelude-ls@1.1.2: {}
@@ -25085,6 +25129,8 @@ snapshots:
web-streams-polyfill@3.3.3: {}
+ web-vitals@4.2.4: {}
+
web3-bzz@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10):
dependencies:
'@types/node': 12.20.55
diff --git a/sdk/package.json b/sdk/package.json
index 2a79b6f5d..9a67628dc 100644
--- a/sdk/package.json
+++ b/sdk/package.json
@@ -27,7 +27,7 @@
"dependencies": {
"@acre-btc/contracts": "workspace:*",
"@keep-network/tbtc-v2.ts": "2.5.0-dev.3",
- "@orangekit/sdk": "1.0.0-beta.18",
+ "@orangekit/sdk": "1.0.0-beta.19",
"ethers": "6.10.0",
"ethers-v5": "npm:ethers@^5.5.2"
}
diff --git a/sdk/src/acre.ts b/sdk/src/acre.ts
index d1a84eabd..652dc5a25 100644
--- a/sdk/src/acre.ts
+++ b/sdk/src/acre.ts
@@ -85,7 +85,9 @@ class Acre {
const orangeKit = await OrangeKitSdk.init(
Number(ethereumChainId),
ethereumRpcUrl,
- new GelatoTransactionSender(gelatoApiKey),
+ new GelatoTransactionSender(gelatoApiKey, {
+ backoffRetrier: { retries: 7, backoffStepMs: 3000 },
+ }),
)
const contracts = await getEthereumContracts(
diff --git a/sdk/src/lib/api/HttpApi.ts b/sdk/src/lib/api/HttpApi.ts
index d1759d6d2..eb9be45ca 100644
--- a/sdk/src/lib/api/HttpApi.ts
+++ b/sdk/src/lib/api/HttpApi.ts
@@ -1,11 +1,48 @@
+import { backoffRetrier, RetryOptions } from "../utils"
+
/**
* Represents an abstract HTTP API.
*/
export default abstract class HttpApi {
#apiUrl: string
- constructor(apiUrl: string) {
+ /**
+ * Retry options for API requests.
+ */
+ #retryOptions: RetryOptions
+
+ constructor(
+ apiUrl: string,
+ retryOptions: RetryOptions = {
+ retries: 5,
+ backoffStepMs: 1000,
+ },
+ ) {
this.#apiUrl = apiUrl
+ this.#retryOptions = retryOptions
+ }
+
+ /**
+ * Makes an HTTP request with retry logic.
+ * @param requestFn Function that returns a Promise of the HTTP response.
+ * @returns The HTTP response.
+ * @throws Error if the request fails after all retries.
+ */
+ async #requestWithRetry(
+ requestFn: () => Promise,
+ ): Promise {
+ return backoffRetrier(
+ this.#retryOptions.retries,
+ this.#retryOptions.backoffStepMs,
+ )(async () => {
+ const response = await requestFn()
+
+ if (!response.ok) {
+ throw new Error(`Request failed: ${await response.text()}`)
+ }
+
+ return response
+ })
}
/**
@@ -18,10 +55,12 @@ export default abstract class HttpApi {
endpoint: string,
requestInit?: RequestInit,
): Promise {
- return fetch(new URL(endpoint, this.#apiUrl), {
- credentials: "include",
- ...requestInit,
- })
+ return this.#requestWithRetry(async () =>
+ fetch(new URL(endpoint, this.#apiUrl), {
+ credentials: "include",
+ ...requestInit,
+ }),
+ )
}
/**
@@ -36,12 +75,14 @@ export default abstract class HttpApi {
body: unknown,
requestInit?: RequestInit,
): Promise {
- return fetch(new URL(endpoint, this.#apiUrl), {
- method: "POST",
- body: JSON.stringify(body),
- credentials: "include",
- headers: { "Content-Type": "application/json" },
- ...requestInit,
- })
+ return this.#requestWithRetry(async () =>
+ fetch(new URL(endpoint, this.#apiUrl), {
+ method: "POST",
+ body: JSON.stringify(body),
+ credentials: "include",
+ headers: { "Content-Type": "application/json" },
+ ...requestInit,
+ }),
+ )
}
}
diff --git a/sdk/src/lib/api/TbtcApi.ts b/sdk/src/lib/api/TbtcApi.ts
index 931e36601..c4ce503c4 100644
--- a/sdk/src/lib/api/TbtcApi.ts
+++ b/sdk/src/lib/api/TbtcApi.ts
@@ -38,12 +38,11 @@ export default class TbtcApi extends HttpApi {
* otherwise.
*/
async saveReveal(revealData: SaveRevealRequest): Promise {
- const response = await this.postRequest("reveals", revealData)
-
- if (!response.ok)
- throw new Error(
- `Reveal not saved properly in the database, response: ${response.status}`,
- )
+ const response = await this.postRequest("reveals", revealData).catch(
+ (error) => {
+ throw new Error(`Failed to save reveal: ${error}`)
+ },
+ )
const { success } = (await response.json()) as { success: boolean }
@@ -60,11 +59,11 @@ export default class TbtcApi extends HttpApi {
depositStatus: DepositStatus
fundingOutpoint: BitcoinTxOutpoint
}> {
- const response = await this.postRequest("deposits", depositData)
- if (!response.ok)
- throw new Error(
- `Bitcoin deposit creation failed, response: ${response.status}`,
- )
+ const response = await this.postRequest("deposits", depositData).catch(
+ (error) => {
+ throw new Error(`Failed to create deposit: ${error}`)
+ },
+ )
const responseData = (await response.json()) as CreateDepositResponse
@@ -85,10 +84,9 @@ export default class TbtcApi extends HttpApi {
async getDepositsByOwner(depositOwner: ChainIdentifier): Promise {
const response = await this.getRequest(
`deposits/${depositOwner.identifierHex}`,
- )
-
- if (!response.ok)
- throw new Error(`Failed to fetch deposits: ${response.status}`)
+ ).catch((error) => {
+ throw new Error(`Failed to fetch deposits: ${error}`)
+ })
const responseData = (await response.json()) as Deposit[]
diff --git a/solidity/README.md b/solidity/README.md
index e842e5564..da86c24f0 100644
--- a/solidity/README.md
+++ b/solidity/README.md
@@ -18,7 +18,7 @@ pnpm install
### Testing
-To run the test execute:
+To run the tests execute:
```
$ pnpm test