Skip to content

Commit

Permalink
feat: add validator details page
Browse files Browse the repository at this point in the history
  • Loading branch information
icfor committed Mar 1, 2024
1 parent 499d6ed commit af55c5b
Show file tree
Hide file tree
Showing 12 changed files with 163 additions and 40 deletions.
5 changes: 4 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Inter } from "next/font/google";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";

import BaseWrapper from "@/features/core/components/base-wrapper";
import { StakingProvider } from "@/features/staking/context/provider";
import {
dashboardUrl,
Expand All @@ -33,7 +34,9 @@ export default function RootLayout({
<html lang="en">
<body className={inter.className}>
<AbstraxionProvider config={abstraxionConfig}>
<StakingProvider>{children}</StakingProvider>
<StakingProvider>
<BaseWrapper>{children}</BaseWrapper>
</StakingProvider>
</AbstraxionProvider>
<ToastContainer closeOnClick />
</body>
Expand Down
11 changes: 2 additions & 9 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import StakingPage from "@/features/staking/components/page";
import StakingPage from "@/features/staking/components/main-page";

export default function Page() {
return (
<main className="m-auto flex min-h-screen max-w-xs flex-col items-center justify-center gap-4 p-4">
<h1 className="text-2xl font-bold tracking-tighter text-black dark:text-white">
XION Staking
</h1>
<StakingPage />
</main>
);
return <StakingPage />;
}
5 changes: 5 additions & 0 deletions src/app/validator/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import ValidatorPage from "@/features/staking/components/validator-page";

export default function Page() {
return <ValidatorPage />;
}
34 changes: 34 additions & 0 deletions src/features/core/components/base-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"use client";

import {
Abstraxion,
useAbstraxionAccount,
useModal,
} from "@burnt-labs/abstraxion";

import LoggedOut from "./logged-out";

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const { isConnected } = useAbstraxionAccount();
const [showAbstraction, setShowAbstraxion] = useModal();

return (
<main className="m-auto flex min-h-screen max-w-xs flex-col items-center justify-center gap-4 p-4">
<h1 className="text-2xl font-bold tracking-tighter text-black dark:text-white">
XION Staking
</h1>
{isConnected ? children : <LoggedOut />}
{showAbstraction && (
<Abstraxion
onClose={() => {
setShowAbstraxion(false);
}}
/>
)}
</main>
);
}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@ const ValidatorItem = ({

return (
<div style={{ border: "solid 1ps white", marginBottom: 10 }}>
<div>
<b>{validator.description.moniker}</b>
</div>
<div>{validator.operatorAddress}</div>
<Link href={`/validator?address=${validator.operatorAddress}`}>
<div>
<b>{validator.description.moniker}</b>
</div>
<div>{validator.operatorAddress}</div>
</Link>
<div>
<Button disabled={disabled} onClick={onStake} structure="naked">
Stake here
Expand Down Expand Up @@ -239,7 +241,7 @@ function StakingPage() {
</Link>
<DebugAccount />
</div>
{validators && (
{!!validators?.items.length && (
<div>
<div>Validators:</div>
<div className="max-h-[200px] max-w-max overflow-auto">
Expand Down
25 changes: 0 additions & 25 deletions src/features/staking/components/page.tsx

This file was deleted.

53 changes: 53 additions & 0 deletions src/features/staking/components/validator-page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"use client";

import { BondStatus } from "cosmjs-types/cosmos/staking/v1beta1/staking";
import { useSearchParams } from "next/navigation";
import { useEffect, useState } from "react";

import { getValidatorDetailsAction } from "../context/actions";
import { useStaking } from "../context/hooks";

export default function ValidatorPage() {
const searchParams = useSearchParams();
const address = searchParams.get("address");
const stakingRef = useStaking();

const [validatorDetails, setValidatorDetails] = useState<Awaited<
ReturnType<typeof getValidatorDetailsAction>
> | null>(null);

useEffect(() => {
(async () => {
if (address) {
const validatorDetailsResult = await getValidatorDetailsAction(
address,
stakingRef.staking,
);

setValidatorDetails(validatorDetailsResult);
}
})();
}, [address, stakingRef]);

if (!validatorDetails) {
return <div>Loading ...</div>;
}

return (
<div>
<div>{address}</div>
<div>{validatorDetails.description.moniker}</div>
<div>{validatorDetails.description.details}</div>
<div>{validatorDetails.description.identity}</div>
<div>{validatorDetails.description.securityContact}</div>
<div>{validatorDetails.description.website}</div>
<div>Jailed: {validatorDetails.jailed.toString()}</div>
<div>
Status:{" "}
{validatorDetails.status === BondStatus.BOND_STATUS_BONDED
? "Bonded"
: validatorDetails.status}
</div>
</div>
);
}
17 changes: 17 additions & 0 deletions src/features/staking/context/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
getDelegations,
getRewards,
getUnbondingDelegations,
getValidatorDetails,
getValidatorsList,
setRedelegate,
stakeAmount,
Expand All @@ -17,6 +18,7 @@ import {
addUnbondings,
setIsInfoLoading,
setTokens,
setValidatorDetails,
setValidators,
} from "./reducer";
import type { StakingContextType, Unbonding } from "./state";
Expand Down Expand Up @@ -157,3 +159,18 @@ export const setRedelegateAction = async (

await fetchStakingDataAction(delegatorAddress, staking);
};

export const getValidatorDetailsAction = async (
validatorAddress: string,
staking: StakingContextType,
) => {
if (staking.state.validatorDetails?.operatorAddress === validatorAddress) {
return staking.state.validatorDetails;
}

const details = await getValidatorDetails(validatorAddress);

staking.dispatch(setValidatorDetails(details));

return details;
};
18 changes: 18 additions & 0 deletions src/features/staking/context/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export type StakingAction =
| {
content: StakingState["tokens"];
type: "SET_TOKENS";
}
| {
content: StakingState["validatorDetails"];
type: "SET_VALIDATOR_DETAILS";
};

type Content<T extends StakingAction["type"]> = Extract<
Expand Down Expand Up @@ -69,6 +73,13 @@ export const addUnbondings = (
type: "ADD_UNBONDINGS",
});

export const setValidatorDetails = (
content: Content<"SET_VALIDATOR_DETAILS">,
): StakingAction => ({
content,
type: "SET_VALIDATOR_DETAILS",
});

// Used for pagination
const getUniqueValidators = (
validators: NonNullable<StakingState["validators"]>["items"],
Expand Down Expand Up @@ -192,6 +203,13 @@ export const reducer = (state: StakingState, action: StakingAction) => {
};
}

case "SET_VALIDATOR_DETAILS": {
return {
...state,
validatorDetails: action.content,
};
}

default:
action satisfies never;

Expand Down
2 changes: 2 additions & 0 deletions src/features/staking/context/state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export type StakingState = {
isInfoLoading: boolean;
tokens: Coin | null;
unbondings: Paginated<Unbonding>;
validatorDetails: null | Validator;
validators: Paginated<Validator>;
};

Expand All @@ -43,6 +44,7 @@ export const defaultState: StakingState = {
isInfoLoading: false,
tokens: null,
unbondings: null,
validatorDetails: null,
validators: null,
};

Expand Down
21 changes: 21 additions & 0 deletions src/features/staking/lib/core/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
} from "@cosmjs/stargate";
import BigNumber from "bignumber.js";
import { MsgWithdrawDelegatorReward } from "cosmjs-types/cosmos/distribution/v1beta1/tx";
import type { Validator } from "cosmjs-types/cosmos/staking/v1beta1/staking";
import {
MsgBeginRedelegate,
MsgDelegate,
Expand All @@ -27,6 +28,26 @@ export const getValidatorsList = async () => {
return await queryClient.staking.validators("BOND_STATUS_BONDED");
};

let validatorDetailsRequest: [string, Promise<Validator>] | null = null;

export const getValidatorDetails = async (address: string) => {
if (validatorDetailsRequest?.[0] === address) {
return validatorDetailsRequest[1];
}

const queryClient = await getStakingQueryClient();

const promise = queryClient.staking.validator(address).then((resp) => {
validatorDetailsRequest = null;

return resp.validator;
});

validatorDetailsRequest = [address, promise];

return promise;
};

export const getBalance = async (address: string) => {
const client = await StargateClient.connect(rpcEndpoint);

Expand Down

0 comments on commit af55c5b

Please sign in to comment.