+ Your access to this Website and its Services is restricted.
+
+
+ It appears that you are located in a jurisdiction subject to
+ restrictions under our{" "}
+
+ Terms of Service
+
+ . As a result, you are not permitted to use or access certain Services
+ on this Website. However, you are still allowed to use the Unstake and
+ Withdraw functions.
+
+
+ Any attempt to bypass these restrictions, including the use of VPNs or
+ similar technologies, is strictly prohibited.
+
+
+ ) : (
+
+ );
+};
diff --git a/apps/staking/src/components/WalletButton/index.tsx b/apps/staking/src/components/WalletButton/index.tsx
index 594d3871d1..74ad476d39 100644
--- a/apps/staking/src/components/WalletButton/index.tsx
+++ b/apps/staking/src/components/WalletButton/index.tsx
@@ -28,10 +28,7 @@ import {
Collection,
} from "react-aria-components";
-import {
- REGION_BLOCKED_SEGMENT,
- VPN_BLOCKED_SEGMENT,
-} from "../../config/isomorphic";
+import { VPN_BLOCKED_SEGMENT } from "../../config/isomorphic";
import {
StateType as ApiStateType,
type States,
@@ -52,8 +49,7 @@ type Props = Omit, "onClick" | "children">;
export const WalletButton = (props: Props) => {
const segment = useSelectedLayoutSegment();
- const isBlocked =
- segment === REGION_BLOCKED_SEGMENT || segment === VPN_BLOCKED_SEGMENT;
+ const isBlocked = segment === VPN_BLOCKED_SEGMENT;
// eslint-disable-next-line unicorn/no-null
return isBlocked ? null : ;
diff --git a/apps/staking/src/config/isomorphic.ts b/apps/staking/src/config/isomorphic.ts
index ca50d0a57f..eb77bfdd81 100644
--- a/apps/staking/src/config/isomorphic.ts
+++ b/apps/staking/src/config/isomorphic.ts
@@ -13,18 +13,19 @@
export const IS_PRODUCTION_BUILD = process.env.NODE_ENV === "production";
/**
- * Region-blocked requests will be redirected here. This is used in the
+ * Region or VPN-blocked requests will be redirected here if they are eligible
+ * for "restricted mode" (aka only allowing withdrawals). This is used in the
* middleware to implement the block, and also consumed in any components that
* are part of the page layout but need to know if the request is blocked from
* accessing the app, such as the WalletButton in the app header.
*
* Don't change unless you also change the relevant app route path to match.
*/
-export const REGION_BLOCKED_SEGMENT = "region-blocked";
+export const RESTRICTED_MODE_SEGMENT = "restricted-mode";
/**
- * Similar to `REGION_BLOCKED_SEGMENT`; this is where vpn-blocked traffic will
- * be rewritten to.
+ * Similar to `RESTRICTED_MODE_SEGMENT`; this is where vpn-blocked traffic will
+ * be rewritten to if it isn't eligible for restricted mode.
*
* Don't change unless you also change the relevant app route path to match.
*/
diff --git a/apps/staking/src/config/server.ts b/apps/staking/src/config/server.ts
index ef3af27bfe..a41c75da03 100644
--- a/apps/staking/src/config/server.ts
+++ b/apps/staking/src/config/server.ts
@@ -9,13 +9,30 @@ import "server-only";
*/
const demand = (key: string): string => {
const value = process.env[key];
- if (value && value !== "") {
- return value;
- } else {
+ if (value === undefined || value === "") {
throw new MissingEnvironmentError(key);
+ } else {
+ return value;
}
};
+const fromCsv = (value: string): string[] =>
+ value.split(",").map((entry) => entry.toLowerCase().trim());
+
+const transform = (key: string, fn: (value: string | undefined) => T): T => {
+ const value = process.env[key];
+ return fn(value === "" ? undefined : value);
+};
+
+const transformOr = (
+ key: string,
+ fn: (value: string) => T,
+ defaultValue: T,
+): T => transform(key, (value) => (value ? fn(value) : defaultValue));
+
+const getOr = (key: string, defaultValue: string): string =>
+ transform(key, (value) => value ?? defaultValue);
+
/**
* Indicates that this server is the live customer-facing production server.
*/
@@ -36,15 +53,8 @@ export const WALLETCONNECT_PROJECT_ID = demandInProduction(
);
export const RPC = process.env.RPC;
export const IS_MAINNET = process.env.IS_MAINNET !== undefined;
-export const HERMES_URL =
- process.env.HERMES_URL ?? "https://hermes.pyth.network";
-export const BLOCKED_REGIONS =
- process.env.BLOCKED_REGIONS === undefined ||
- process.env.BLOCKED_REGIONS === ""
- ? []
- : process.env.BLOCKED_REGIONS.split(",").map((region) =>
- region.toLowerCase().trim(),
- );
+export const HERMES_URL = getOr("HERMES_URL", "https://hermes.pyth.network");
+export const BLOCKED_REGIONS = transformOr("BLOCKED_REGIONS", fromCsv, []);
export const PROXYCHECK_API_KEY = demandInProduction("PROXYCHECK_API_KEY");
class MissingEnvironmentError extends Error {
diff --git a/apps/staking/src/middleware.ts b/apps/staking/src/middleware.ts
index 2977f0e1ef..032296fa46 100644
--- a/apps/staking/src/middleware.ts
+++ b/apps/staking/src/middleware.ts
@@ -2,12 +2,12 @@ import { type NextRequest, NextResponse } from "next/server";
import ProxyCheck from "proxycheck-ts";
import {
- REGION_BLOCKED_SEGMENT,
+ RESTRICTED_MODE_SEGMENT,
VPN_BLOCKED_SEGMENT,
} from "./config/isomorphic";
import { BLOCKED_REGIONS, PROXYCHECK_API_KEY } from "./config/server";
-const PROXY_BLOCK_PATH = `/${REGION_BLOCKED_SEGMENT}`;
+const RESTRICTED_MODE_PATH = `/${RESTRICTED_MODE_SEGMENT}`;
const VPN_BLOCK_PATH = `/${VPN_BLOCKED_SEGMENT}`;
const proxyCheckClient = PROXYCHECK_API_KEY
@@ -15,10 +15,10 @@ const proxyCheckClient = PROXYCHECK_API_KEY
: undefined;
export const middleware = async (request: NextRequest) => {
- if (isRegionBlocked(request)) {
- return rewrite(request, PROXY_BLOCK_PATH);
- } else if (await isProxyBlocked(request)) {
+ if (await isProxyBlocked(request)) {
return rewrite(request, VPN_BLOCK_PATH);
+ } else if (isRegionBlocked(request)) {
+ return rewrite(request, RESTRICTED_MODE_PATH);
} else if (isBlockedSegment(request)) {
return rewrite(request, "/not-found");
} else {
@@ -43,12 +43,12 @@ const isProxyBlocked = async ({ ip }: NextRequest) => {
};
const isBlockedSegment = ({ nextUrl: { pathname } }: NextRequest) =>
- pathname.startsWith(`/${REGION_BLOCKED_SEGMENT}`) ||
- pathname.startsWith(`/${VPN_BLOCKED_SEGMENT}`);
+ pathname.startsWith(`/${VPN_BLOCKED_SEGMENT}`) ||
+ pathname.startsWith(`/${RESTRICTED_MODE_SEGMENT}`);
export const config = {
// Next.js requires that this is a static string and fails to read it if it's
// a String.raw, so let's disable this rule
// eslint-disable-next-line unicorn/prefer-string-raw
- matcher: ["/((?!_next/static|_next/image|.*\\.).*)"],
+ matcher: ["/((?!_next/static|_next/image|api/|terms-of-service|.*\\.).*)"],
};