Skip to content

Commit

Permalink
Merge pull request #1998 from cprussin/enable-governance-only
Browse files Browse the repository at this point in the history
feat(staking): allow governance only for some regions
  • Loading branch information
cprussin authored Oct 7, 2024
2 parents 4b08055 + 0c65ca2 commit 12536a5
Show file tree
Hide file tree
Showing 11 changed files with 110 additions and 54 deletions.
1 change: 1 addition & 0 deletions apps/staking/src/app/geo-blocked/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { GeoBlockedHome as default } from "../../components/Home";
1 change: 1 addition & 0 deletions apps/staking/src/app/governance-only/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { GovernanceOnlyHome as default } from "../../components/Home";
1 change: 0 additions & 1 deletion apps/staking/src/app/restricted-mode/page.tsx

This file was deleted.

16 changes: 9 additions & 7 deletions apps/staking/src/components/AccountSummary/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ type Props = {
availableRewards: bigint;
expiringRewards: Date | undefined;
availableToWithdraw: bigint;
restrictedMode?: boolean | undefined;
enableGovernance: boolean;
enableOis: boolean;
integrityStakingWarmup: bigint;
integrityStakingStaked: bigint;
integrityStakingCooldown: bigint;
Expand All @@ -61,7 +62,8 @@ export const AccountSummary = ({
availableToWithdraw,
availableRewards,
expiringRewards,
restrictedMode,
enableGovernance,
enableOis,
integrityStakingWarmup,
integrityStakingStaked,
integrityStakingCooldown,
Expand Down Expand Up @@ -131,7 +133,7 @@ export const AccountSummary = ({
</>
)}
<div className="mt-3 flex flex-row items-center gap-4 sm:mt-8">
{!restrictedMode && (
{(enableGovernance || enableOis) && (
<TransferButton
actionName="Add tokens"
actionDescription="Add funds to your balance"
Expand Down Expand Up @@ -167,7 +169,7 @@ export const AccountSummary = ({
className="xl:hidden"
/>
)}
{!restrictedMode && (
{enableOis && (
<DialogTrigger>
<Button variant="secondary" className="xl:hidden">
Claim
Expand All @@ -188,7 +190,7 @@ export const AccountSummary = ({
)}
</div>
</div>
{restrictedMode && api.type === ApiStateType.Loaded && (
{!enableOis && api.type === ApiStateType.Loaded && (
<OisUnstake
api={api}
className="max-w-sm xl:hidden"
Expand All @@ -208,7 +210,7 @@ export const AccountSummary = ({
<WithdrawButton api={api} max={availableToWithdraw} size="small" />
}
/>
{restrictedMode && api.type === ApiStateType.Loaded && (
{!enableOis && api.type === ApiStateType.Loaded && (
<OisUnstake
api={api}
warmup={integrityStakingWarmup}
Expand All @@ -218,7 +220,7 @@ export const AccountSummary = ({
currentEpoch={currentEpoch}
/>
)}
{!restrictedMode && (
{enableOis && (
<BalanceCategory
name="Available Rewards"
amount={availableRewards}
Expand Down
35 changes: 19 additions & 16 deletions apps/staking/src/components/Dashboard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ type Props = {
integrityStakingPublishers: ComponentProps<
typeof OracleIntegrityStaking
>["publishers"];
restrictedMode?: boolean | undefined;
enableGovernance: boolean;
enableOis: boolean;
};

export const Dashboard = ({
Expand All @@ -65,7 +66,8 @@ export const Dashboard = ({
integrityStakingPublishers,
unlockSchedule,
yieldRate,
restrictedMode,
enableGovernance,
enableOis,
}: Props) => {
const [tab, setTab] = useState<TabId>(TabIds.Empty);

Expand Down Expand Up @@ -138,7 +140,7 @@ export const Dashboard = ({
<>
<main
className={clsx("flex w-full flex-col gap-8 xl:px-4 xl:py-6", {
"sm:gap-0": restrictedMode,
"sm:gap-0": !enableOis,
})}
>
<AccountSummary
Expand All @@ -151,25 +153,15 @@ export const Dashboard = ({
availableToWithdraw={availableToWithdraw}
availableRewards={availableRewards}
expiringRewards={expiringRewards}
restrictedMode={restrictedMode}
enableGovernance={enableGovernance}
enableOis={enableOis}
integrityStakingWarmup={integrityStakingWarmup}
integrityStakingStaked={integrityStakingStaked}
integrityStakingCooldown={integrityStakingCooldown}
integrityStakingCooldown2={integrityStakingCooldown2}
currentEpoch={currentEpoch}
/>
{restrictedMode ? (
<Governance
api={api}
currentEpoch={currentEpoch}
availableToStake={availableToStakeGovernance}
warmup={governance.warmup}
staked={governance.staked}
cooldown={governance.cooldown}
cooldown2={governance.cooldown2}
restrictedMode
/>
) : (
{enableOis ? (
<Tabs
selectedKey={tab}
onSelectionChange={setTab}
Expand Down Expand Up @@ -229,6 +221,17 @@ export const Dashboard = ({
/>
</TabPanel>
</Tabs>
) : (
<Governance
api={api}
currentEpoch={currentEpoch}
availableToStake={availableToStakeGovernance}
warmup={governance.warmup}
staked={governance.staked}
cooldown={governance.cooldown}
cooldown2={governance.cooldown2}
allowStaking={enableGovernance}
/>
)}
</main>
<Disclosure />
Expand Down
8 changes: 4 additions & 4 deletions apps/staking/src/components/Governance/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type Props = {
staked: bigint;
cooldown: bigint;
cooldown2: bigint;
restrictedMode?: boolean | undefined;
allowStaking?: boolean | undefined;
};

export const Governance = ({
Expand All @@ -23,10 +23,10 @@ export const Governance = ({
staked,
cooldown,
cooldown2,
restrictedMode,
allowStaking,
}: Props) => (
<ProgramSection
className={clsx({ "border-t sm:border-t-0": restrictedMode })}
className={clsx({ "border-t sm:border-t-0": !allowStaking })}
name="Pyth Governance"
helpDialog={<GovernanceGuide />}
tagline="Vote and Influence the Network"
Expand All @@ -47,7 +47,7 @@ export const Governance = ({
unstake:
api.type === ApiStateType.Loaded ? api.unstakeGovernance : undefined,
unstakeDescription: "Unstake tokens from the Governance program",
...(!restrictedMode && {
...(allowStaking && {
stake:
api.type === ApiStateType.Loaded ? api.stakeGovernance : undefined,
stakeDescription: "Stake funds to participate in governance votes",
Expand Down
43 changes: 32 additions & 11 deletions apps/staking/src/components/Home/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,34 @@ const ONE_SECOND_IN_MS = 1000;
const ONE_MINUTE_IN_MS = 60 * ONE_SECOND_IN_MS;
const REFRESH_INTERVAL = 1 * ONE_MINUTE_IN_MS;

export const Home = () => <HomeImpl />;
export const RestrictedMode = () => <HomeImpl restrictedMode />;
export const Home = () => <HomeImpl enableGovernance enableOis />;
export const GeoBlockedHome = () => <HomeImpl />;
export const GovernanceOnlyHome = () => <HomeImpl enableGovernance />;

type HomeImplProps = {
restrictedMode?: boolean | undefined;
enableGovernance?: boolean | undefined;
enableOis?: boolean | undefined;
};

export const HomeImpl = ({ restrictedMode }: HomeImplProps) => {
export const HomeImpl = ({ enableGovernance, enableOis }: HomeImplProps) => {
const isSSR = useIsSSR();

return isSSR ? <Loading /> : <MountedHome restrictedMode={restrictedMode} />;
return isSSR ? (
<Loading />
) : (
<MountedHome
enableGovernance={enableGovernance ?? false}
enableOis={enableOis ?? false}
/>
);
};

type MountedHomeProps = {
restrictedMode?: boolean | undefined;
enableGovernance: boolean;
enableOis: boolean;
};

const MountedHome = ({ restrictedMode }: MountedHomeProps) => {
const MountedHome = ({ enableGovernance, enableOis }: MountedHomeProps) => {
const api = useApi();

switch (api.type) {
Expand All @@ -56,20 +66,26 @@ const MountedHome = ({ restrictedMode }: MountedHomeProps) => {
case ApiStateType.LoadedNoStakeAccount:
case ApiStateType.Loaded: {
return (
<StakeAccountLoadedHome restrictedMode={restrictedMode} api={api} />
<StakeAccountLoadedHome
enableGovernance={enableGovernance}
enableOis={enableOis}
api={api}
/>
);
}
}
};

type StakeAccountLoadedHomeProps = {
api: States[ApiStateType.Loaded] | States[ApiStateType.LoadedNoStakeAccount];
restrictedMode?: boolean | undefined;
enableGovernance: boolean;
enableOis: boolean;
};

const StakeAccountLoadedHome = ({
api,
restrictedMode,
enableGovernance,
enableOis,
}: StakeAccountLoadedHomeProps) => {
const data = useData(api.dashboardDataCacheKey, api.loadData, {
refreshInterval: REFRESH_INTERVAL,
Expand All @@ -87,7 +103,12 @@ const StakeAccountLoadedHome = ({

case DashboardDataStateType.Loaded: {
return (
<Dashboard {...data.data} api={api} restrictedMode={restrictedMode} />
<Dashboard
{...data.data}
api={api}
enableGovernance={enableGovernance}
enableOis={enableOis}
/>
);
}
}
Expand Down
8 changes: 6 additions & 2 deletions apps/staking/src/components/Root/restricted-region-banner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@

import { useSelectedLayoutSegment } from "next/navigation";

import { RESTRICTED_MODE_SEGMENT } from "../../config/isomorphic";
import {
GEO_BLOCKED_SEGMENT,
GOVERNANCE_ONLY_SEGMENT,
} from "../../config/isomorphic";
import { Link } from "../Link";

export const RestrictedRegionBanner = () => {
const segment = useSelectedLayoutSegment();
const isRestrictedMode = segment === RESTRICTED_MODE_SEGMENT;
const isRestrictedMode =
segment === GEO_BLOCKED_SEGMENT || segment === GOVERNANCE_ONLY_SEGMENT;

return isRestrictedMode ? (
<div className="mx-auto mt-8 flex max-w-3xl flex-col gap-2 bg-red-900 px-8 py-6">
Expand Down
17 changes: 12 additions & 5 deletions apps/staking/src/config/isomorphic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,26 @@
export const IS_PRODUCTION_BUILD = process.env.NODE_ENV === "production";

/**
* 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
* Region blocked requests will be rewritten here. 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 RESTRICTED_MODE_SEGMENT = "restricted-mode";
export const GEO_BLOCKED_SEGMENT = "geo-blocked";

/**
* Similar to `RESTRICTED_MODE_SEGMENT`; this is where vpn-blocked traffic will
* be rewritten to if it isn't eligible for restricted mode.
* Similar to `GEO_BLOCKED_SEGMENT`; this is where governance-only region
* requests are rewritten to.
*
* Don't change unless you also change the relevant app route path to match.
*/
export const GOVERNANCE_ONLY_SEGMENT = "governance-only";

/**
* Similar to `GEO_BLOCKED_SEGMENT`; this is where vpn-blocked traffic will be
* rewritten to.
*
* Don't change unless you also change the relevant app route path to match.
*/
Expand Down
5 changes: 5 additions & 0 deletions apps/staking/src/config/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ export const WALLETCONNECT_PROJECT_ID = demandInProduction(
export const MAINNET_RPC = process.env.MAINNET_RPC;
export const HERMES_URL = getOr("HERMES_URL", "https://hermes.pyth.network");
export const BLOCKED_REGIONS = transformOr("BLOCKED_REGIONS", fromCsv, []);
export const GOVERNANCE_ONLY_REGIONS = transformOr(
"GOVERNANCE_ONLY_REGIONS",
fromCsv,
[],
);
export const PROXYCHECK_API_KEY = demandInProduction("PROXYCHECK_API_KEY");

class MissingEnvironmentError extends Error {
Expand Down
29 changes: 21 additions & 8 deletions apps/staking/src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,31 @@ import { type NextRequest, NextResponse } from "next/server";
import ProxyCheck from "proxycheck-ts";

import {
RESTRICTED_MODE_SEGMENT,
GEO_BLOCKED_SEGMENT,
GOVERNANCE_ONLY_SEGMENT,
VPN_BLOCKED_SEGMENT,
} from "./config/isomorphic";
import { BLOCKED_REGIONS, PROXYCHECK_API_KEY } from "./config/server";
import {
BLOCKED_REGIONS,
GOVERNANCE_ONLY_REGIONS,
PROXYCHECK_API_KEY,
} from "./config/server";

const RESTRICTED_MODE_PATH = `/${RESTRICTED_MODE_SEGMENT}`;
const VPN_BLOCK_PATH = `/${VPN_BLOCKED_SEGMENT}`;
const GEO_BLOCKED_PATH = `/${GEO_BLOCKED_SEGMENT}`;
const GOVERNANCE_ONLY_PATH = `/${GOVERNANCE_ONLY_SEGMENT}`;
const VPN_BLOCKED_PATH = `/${VPN_BLOCKED_SEGMENT}`;

const proxyCheckClient = PROXYCHECK_API_KEY
? new ProxyCheck({ api_key: PROXYCHECK_API_KEY })
: undefined;

export const middleware = async (request: NextRequest) => {
if (await isProxyBlocked(request)) {
return rewrite(request, VPN_BLOCK_PATH);
return rewrite(request, VPN_BLOCKED_PATH);
} else if (isGovernanceOnlyRegion(request)) {
return rewrite(request, GOVERNANCE_ONLY_PATH);
} else if (isRegionBlocked(request)) {
return rewrite(request, RESTRICTED_MODE_PATH);
return rewrite(request, GEO_BLOCKED_PATH);
} else if (isBlockedSegment(request)) {
return rewrite(request, "/not-found");
} else {
Expand All @@ -29,6 +37,10 @@ export const middleware = async (request: NextRequest) => {
const rewrite = (request: NextRequest, path: string) =>
NextResponse.rewrite(new URL(path, request.url));

const isGovernanceOnlyRegion = ({ geo }: NextRequest) =>
geo?.country !== undefined &&
GOVERNANCE_ONLY_REGIONS.includes(geo.country.toLowerCase());

const isRegionBlocked = ({ geo }: NextRequest) =>
geo?.country !== undefined &&
BLOCKED_REGIONS.includes(geo.country.toLowerCase());
Expand All @@ -43,8 +55,9 @@ const isProxyBlocked = async ({ ip }: NextRequest) => {
};

const isBlockedSegment = ({ nextUrl: { pathname } }: NextRequest) =>
pathname.startsWith(`/${VPN_BLOCKED_SEGMENT}`) ||
pathname.startsWith(`/${RESTRICTED_MODE_SEGMENT}`);
pathname.startsWith(VPN_BLOCKED_PATH) ||
pathname.startsWith(GEO_BLOCKED_PATH) ||
pathname.startsWith(GOVERNANCE_ONLY_PATH);

export const config = {
// Next.js requires that this is a static string and fails to read it if it's
Expand Down

0 comments on commit 12536a5

Please sign in to comment.