Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.

Commit cc8a496

Browse files
committed
fix: allow failed address checker users to perform unbond
1 parent 9728e9d commit cc8a496

10 files changed

Lines changed: 169 additions & 56 deletions

File tree

src/ui/baby/state/StakingState.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import { useWalletService } from "@/ui/baby/hooks/services/useWalletService";
88
import { validateDecimalPoints } from "@/ui/common/components/Staking/Form/validation/validation";
99
import { useError } from "@/ui/common/context/Error/ErrorProvider";
1010
import { usePrice } from "@/ui/common/hooks/client/api/usePrices";
11+
import { useHealthCheck } from "@/ui/common/hooks/useHealthCheck";
1112
import { useLogger } from "@/ui/common/hooks/useLogger";
1213
import { MultistakingFormFields } from "@/ui/common/state/MultistakingState";
14+
import { GEO_BLOCK_MESSAGE } from "@/ui/common/types/services/healthCheck";
1315
import {
1416
createBalanceValidator,
1517
createMinAmountValidator,
@@ -64,6 +66,10 @@ interface StakingState {
6466
submitForm(): Promise<void>;
6567
resetForm(): void;
6668
calculateFee: (params: Omit<FormData, "feeAmount">) => Promise<number>;
69+
disabled?: {
70+
title: string;
71+
message: string;
72+
};
6773
}
6874

6975
const { StateProvider, useState: useStakingState } =
@@ -79,12 +85,14 @@ const { StateProvider, useState: useStakingState } =
7985
closePreview: () => {},
8086
submitForm: async () => {},
8187
resetForm: () => {},
88+
disabled: undefined,
8289
});
8390

8491
function StakingState({ children }: PropsWithChildren) {
8592
const [step, setStep] = useState<StakingStep>({ name: "initial" });
8693

8794
const { stake, sendTx, estimateStakingFee } = useDelegationService();
95+
const { isGeoBlocked } = useHealthCheck();
8896
const { validatorMap, loading } = useValidatorService();
8997
const { balance } = useWalletService();
9098
const { handleError } = useError();
@@ -104,6 +112,15 @@ function StakingState({ children }: PropsWithChildren) {
104112
[availableBalance],
105113
);
106114

115+
const isDisabled = useMemo(() => {
116+
if (isGeoBlocked) {
117+
return {
118+
title: "Unavailable In Your Region",
119+
message: GEO_BLOCK_MESSAGE,
120+
};
121+
}
122+
}, [isGeoBlocked]);
123+
107124
const fieldSchemas = useMemo(
108125
() =>
109126
[
@@ -253,6 +270,7 @@ function StakingState({ children }: PropsWithChildren) {
253270
submitForm,
254271
resetForm,
255272
closePreview,
273+
disabled: isDisabled,
256274
};
257275
}, [
258276
availableBalance,
@@ -266,6 +284,7 @@ function StakingState({ children }: PropsWithChildren) {
266284
submitForm,
267285
resetForm,
268286
closePreview,
287+
isDisabled,
269288
]);
270289

271290
return <StateProvider value={context}>{children}</StateProvider>;

src/ui/baby/widgets/StakingForm/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export default function StakingForm({
2828
babyPrice,
2929
calculateFee,
3030
showPreview,
31+
disabled,
3132
} = useStakingState();
3233

3334
const handlePreview = ({
@@ -50,7 +51,7 @@ export default function StakingForm({
5051

5152
<SubmitButton disabled={loading} isGeoBlocked={isGeoBlocked} />
5253
<StakingModal />
53-
<FormAlert isGeoBlocked={isGeoBlocked} />
54+
<FormAlert {...disabled} />
5455
</Form>
5556
);
5657
}

src/ui/common/components/Multistaking/MultistakingForm/FormAlert.tsx

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,22 @@
11
import { MdErrorOutline } from "react-icons/md";
22

33
import { Alert } from "@/ui/common/components/Alerts/Alert";
4-
import { STAKING_DISABLED } from "@/ui/common/constants";
5-
import { GEO_BLOCK_MESSAGE } from "@/ui/common/types/services/healthCheck";
64

7-
interface FormAlertProps {
8-
address?: string;
9-
isGeoBlocked: boolean;
10-
}
11-
12-
export const FormAlert = ({ address, isGeoBlocked }: FormAlertProps) => {
13-
const stakingDisabledMessage = (
14-
<>
15-
The Babylon network is under maintenance. New stakes are paused until the
16-
network resumes.
17-
</>
18-
);
19-
20-
const shouldShowAlert = (address && STAKING_DISABLED) || isGeoBlocked;
21-
22-
if (!shouldShowAlert) {
5+
export const FormAlert = ({
6+
title,
7+
message,
8+
}: {
9+
title?: string;
10+
message?: string;
11+
}) => {
12+
if (!title) {
2313
return null;
2414
}
2515

2616
return (
2717
<div className="pt-2">
28-
<Alert
29-
icon={<MdErrorOutline />}
30-
title={
31-
isGeoBlocked ? (
32-
<strong>Unavailable In Your Region.</strong>
33-
) : (
34-
<strong>Staking Currently Unavailable.</strong>
35-
)
36-
}
37-
>
38-
{isGeoBlocked ? GEO_BLOCK_MESSAGE : stakingDisabledMessage}
18+
<Alert icon={<MdErrorOutline />} title={<strong>{title}</strong>}>
19+
{message}
3920
</Alert>
4021
</div>
4122
);

src/ui/common/components/Multistaking/MultistakingForm/MultistakingFormContent.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { HiddenField } from "@babylonlabs-io/core-ui";
22

33
import { AuthGuard } from "@/ui/common/components/Common/AuthGuard";
44
import { MultistakingModal } from "@/ui/common/components/Multistaking/MultistakingModal/MultistakingModal";
5-
import { useBTCWallet } from "@/ui/common/context/wallet/BTCWalletProvider";
65
import { useStakingState } from "@/ui/common/state/StakingState";
76

87
import { AmountSection } from "./AmountSection";
@@ -13,8 +12,7 @@ import { StakingFeesSection } from "./StakingFeesSection";
1312
import { SubmitButton } from "./SubmitButton";
1413

1514
export function MultistakingFormContent() {
16-
const { address } = useBTCWallet();
17-
const { stakingInfo, blocked: isGeoBlocked } = useStakingState();
15+
const { stakingInfo, blocked: isGeoBlocked, disabled } = useStakingState();
1816

1917
return (
2018
<>
@@ -37,7 +35,7 @@ export function MultistakingFormContent() {
3735
<SubmitButton />
3836
</AuthGuard>
3937

40-
<FormAlert address={address} isGeoBlocked={isGeoBlocked} />
38+
<FormAlert {...disabled} />
4139
</div>
4240

4341
<MultistakingModal />

src/ui/common/components/Multistaking/MultistakingForm/SubmitButton.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const BUTTON_STYLES: Record<string, string> = {
1212

1313
export function SubmitButton() {
1414
const { isValid, isValidating, isLoading } = useFormState();
15-
const { blocked: isGeoBlocked } = useStakingState();
15+
const { blocked: isGeoBlocked, disabled: isDisabled } = useStakingState();
1616
const error = useFormError();
1717

1818
const renderText = () => {
@@ -44,7 +44,8 @@ export function SubmitButton() {
4444
isValidating ||
4545
isLoading ||
4646
STAKING_DISABLED ||
47-
isGeoBlocked
47+
isGeoBlocked ||
48+
isDisabled !== undefined
4849
}
4950
>
5051
{renderText()}

src/ui/common/context/wallet/BTCWalletProvider.tsx

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,19 @@ import {
3535
toNetwork,
3636
} from "@/ui/common/utils/wallet";
3737

38+
import { useAddressScreeningService } from "../../hooks/services/useAddressScreeningService";
39+
3840
const btcConfig = getNetworkConfigBTC();
41+
const supportedNetworkMessage =
42+
"Only Native SegWit and Taproot addresses are supported. Please switch the address type in your wallet and try again.";
3943

4044
interface BTCWalletContextProps {
4145
loading: boolean;
4246
network?: networks.Network;
4347
publicKeyNoCoord: string;
4448
address: string;
4549
connected: boolean;
50+
failedBtcAddressRiskAssessment: boolean;
4651
disconnect: () => void;
4752
open: () => void;
4853
getAddress: () => Promise<string>;
@@ -70,6 +75,7 @@ const BTCWalletContext = createContext<BTCWalletContextProps>({
7075
connected: false,
7176
publicKeyNoCoord: "",
7277
address: "",
78+
failedBtcAddressRiskAssessment: false,
7379
disconnect: () => {},
7480
open: () => {},
7581
getAddress: async () => "",
@@ -87,6 +93,8 @@ const BTCWalletContext = createContext<BTCWalletContextProps>({
8793

8894
export const BTCWalletProvider = ({ children }: PropsWithChildren) => {
8995
const [loading, setLoading] = useState(true);
96+
const [failedBtcAddressRiskAssessment, setFailedBtcAddressRiskAssessment] =
97+
useState(false);
9098
const [btcWalletProvider, setBTCWalletProvider] = useState<IBTCProvider>();
9199
const [network, setNetwork] = useState<networks.Network>();
92100
const [publicKeyNoCoord, setPublicKeyNoCoord] = useState("");
@@ -97,24 +105,25 @@ export const BTCWalletProvider = ({ children }: PropsWithChildren) => {
97105
const { open = () => {}, connected } = useWalletConnect();
98106
const logger = useLogger();
99107
const { updateUser } = useSentryUser();
108+
const { screenAddress, clearAddressScreeningResult } =
109+
useAddressScreeningService();
100110

101111
const btcDisconnect = useCallback(() => {
102112
setBTCWalletProvider(undefined);
103113
setNetwork(undefined);
104114
setPublicKeyNoCoord("");
105115
setAddress("");
116+
setFailedBtcAddressRiskAssessment(false);
106117

107118
updateUser({ btcAddress: null });
108-
}, [updateUser]);
119+
clearAddressScreeningResult();
120+
}, [updateUser, clearAddressScreeningResult]);
109121

110122
const connectBTC = useCallback(
111123
async (walletProvider: IBTCProvider | null) => {
112124
if (!walletProvider) return;
113125
setLoading(true);
114126

115-
const supportedNetworkMessage =
116-
"Only Native SegWit and Taproot addresses are supported. Please switch the address type in your wallet and try again.";
117-
118127
try {
119128
const network = await walletProvider.getNetwork();
120129
if (network !== btcConfig.network) {
@@ -164,6 +173,9 @@ export const BTCWalletProvider = ({ children }: PropsWithChildren) => {
164173
throw emptyProcessedPubKeyError;
165174
}
166175

176+
const failedRiskAssessment = await screenAddress(address);
177+
setFailedBtcAddressRiskAssessment(failedRiskAssessment);
178+
167179
setBTCWalletProvider(walletProvider);
168180
setNetwork(toNetwork(network));
169181
setAddress(address);
@@ -192,7 +204,7 @@ export const BTCWalletProvider = ({ children }: PropsWithChildren) => {
192204
});
193205
}
194206
},
195-
[handleError, publicKeyNoCoord, address, logger, updateUser],
207+
[handleError, publicKeyNoCoord, address, logger, updateUser, screenAddress],
196208
);
197209

198210
useEffect(() => {
@@ -288,13 +300,15 @@ export const BTCWalletProvider = ({ children }: PropsWithChildren) => {
288300
network,
289301
publicKeyNoCoord,
290302
address,
303+
failedBtcAddressRiskAssessment,
291304
connected,
292305
open,
293306
disconnect: btcDisconnect,
294307
...btcWalletMethods,
295308
}),
296309
[
297310
loading,
311+
failedBtcAddressRiskAssessment,
298312
connected,
299313
network,
300314
publicKeyNoCoord,

src/ui/common/context/wallet/WalletConnectionProvider.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { useTheme } from "next-themes";
77
import { useCallback, type PropsWithChildren } from "react";
88

99
import { logTermsAcceptance } from "@/ui/common/api/logTermAcceptance";
10-
import { verifyBTCAddress } from "@/ui/common/api/verifyBTCAddress";
1110
import { getNetworkConfigBBN } from "@/ui/common/config/network/bbn";
1211
import { getNetworkConfigBTC } from "@/ui/common/config/network/btc";
1312
import { ClientError, ERROR_CODES } from "@/ui/common/errors";
@@ -20,7 +19,6 @@ const context = typeof window !== "undefined" ? window : {};
2019

2120
const lifecycleHooks = {
2221
acceptTermsOfService: logTermsAcceptance,
23-
verifyBTCAddress: verifyBTCAddress,
2422
};
2523

2624
const config: ChainConfigArr = [
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { useCallback } from "react";
2+
3+
import { verifyBTCAddress } from "../../api/verifyBTCAddress";
4+
import {
5+
clearBtcAddressScreeningResult,
6+
getBtcAddressScreeningResult,
7+
setBtcAddressScreeningResult,
8+
} from "../../utils/local_storage/addressScreeningStorage";
9+
import { useLogger } from "../useLogger";
10+
11+
export function useAddressScreeningService() {
12+
const logger = useLogger();
13+
14+
const screenAddress = useCallback(
15+
async (btcAddress: string) => {
16+
try {
17+
// Check cache first
18+
const cachedResult = getBtcAddressScreeningResult();
19+
if (cachedResult && cachedResult.btcAddress === btcAddress) {
20+
return cachedResult.failedRiskAssessment;
21+
}
22+
23+
// Perform screening
24+
const riskAssessment = await verifyBTCAddress(btcAddress);
25+
const failedRiskAssessment = !riskAssessment;
26+
27+
// Cache result
28+
setBtcAddressScreeningResult(btcAddress, failedRiskAssessment);
29+
30+
logger.info("Address screening completed", {
31+
btcAddress,
32+
failedRiskAssessment,
33+
});
34+
35+
return failedRiskAssessment;
36+
} catch (error: any) {
37+
logger.error(error, { data: { btcAddress } });
38+
return false; // Default to safe
39+
}
40+
},
41+
[logger],
42+
);
43+
44+
const clearAddressScreeningResult = useCallback(() => {
45+
clearBtcAddressScreeningResult();
46+
}, []);
47+
48+
return {
49+
screenAddress,
50+
clearAddressScreeningResult,
51+
};
52+
}

0 commit comments

Comments
 (0)