Skip to content

Commit

Permalink
feat: subnet filters
Browse files Browse the repository at this point in the history
Co-authored-by: Kelvin Steiner <[email protected]>
  • Loading branch information
EdSDR and steinerkelvin committed Oct 15, 2024
1 parent c0b1958 commit 7147db1
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 33 deletions.
6 changes: 3 additions & 3 deletions apps/commune-validator/src/app/(pages)/modules/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Suspense } from "react";

import type { Module } from "~/utils/types";
import { ModuleCard } from "~/app/components/module-card";
import { PaginationControls } from "~/app/components/pagination-controls";
import { ViewControls } from "~/app/components/view-controls";
import { api } from "~/trpc/server";

export default async function Page({
export default async function ModulesPage({
searchParams,
}: {
searchParams: { page?: string; sortBy?: string; order?: string };
Expand All @@ -17,7 +18,6 @@ export default async function Page({
const { modules, metadata } = await api.module.paginatedAll({
page: currentPage,
limit: 24,
// @ts-expect-error - TS doesn't know about sortBy for some reason
sortBy: sortBy,
order: order,
});
Expand All @@ -29,7 +29,7 @@ export default async function Page({
</Suspense>
<div className="mb-16 grid w-full animate-fade-up grid-cols-1 gap-4 backdrop-blur-md animate-delay-700 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{modules.length ? (
modules.map((module) => (
modules.map((module: Module) => (
<ModuleCard
id={module.id}
key={module.id}
Expand Down
54 changes: 42 additions & 12 deletions apps/commune-validator/src/app/(pages)/subnets/page.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,49 @@
import { Suspense } from "react";

import type { Subnet } from "~/utils/types";
import { PaginationControls } from "~/app/components/pagination-controls";
import SubnetCard from "~/app/components/subnet-card";
import { SubnetViewControls } from "~/app/components/subnet-view-controls";
import { api } from "~/trpc/server";

export default async function SubnetsPage() {
const data = await api.subnet.all();
export default async function SubnetsPage({
searchParams,
}: {
searchParams: { page?: string; sortBy?: string; order?: string };
}) {
const currentPage = Number(searchParams.page) || 1;
const sortBy = searchParams.sortBy ?? "id";
const order = searchParams.order === "desc" ? "desc" : "asc";

const { subnets, metadata } = await api.subnet.paginatedAll({
page: currentPage,
limit: 24,
sortBy: sortBy,
order: order,
});

return (
<div className="mb-4 flex w-full flex-col gap-4">
{data.map((subnet) => (
<SubnetCard
key={subnet.id}
founderAddress={subnet.founder}
id={subnet.netuid}
name={subnet.name}
/>
))}
</div>
<>
<Suspense fallback={<div>Loading view controls...</div>}>
<SubnetViewControls />
</Suspense>
<div className="mb-4 flex w-full flex-col gap-4">
{subnets.length ? (
subnets.map((subnet: Subnet) => (
<SubnetCard
key={subnet.id}
founderAddress={subnet.founder}
id={subnet.netuid}
name={subnet.name}
/>
))
) : (
<p>No subnets found</p>
)}
</div>
<Suspense fallback={<div>Loading...</div>}>
<PaginationControls totalPages={metadata.totalPages} />
</Suspense>
</>
);
}
70 changes: 70 additions & 0 deletions apps/commune-validator/src/app/components/subnet-view-controls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"use client";

import { useEffect, useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";

type SortField =
| "id"
| "founderShare"
| "incentiveRatio"
| "proposalRewardTreasuryAllocation"
| "minValidatorStake"
| "createdAt";

type SortOrder = "asc" | "desc";

const sortFieldLabels: Record<SortField, string> = {
id: "ID",
founderShare: "Founder Share",
incentiveRatio: "Incentive Ratio",
proposalRewardTreasuryAllocation: "Proposal Reward Alloc.",
minValidatorStake: "Min. Vali. Stake",
createdAt: "Creation Date",
};

export function SubnetViewControls() {
const router = useRouter();
const searchParams = useSearchParams();
const [sortField, setSortField] = useState<SortField>(
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
(searchParams.get("sortBy") as SortField) ?? "id",
);
const [sortOrder, setSortOrder] = useState<SortOrder>(
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
(searchParams.get("order") as SortOrder) ?? "asc",
);

useEffect(() => {
const newSearchParams = new URLSearchParams(searchParams);
newSearchParams.set("sortBy", sortField);
newSearchParams.set("order", sortOrder);
router.push(`?${newSearchParams.toString()}`, { scroll: false });
}, [sortField, sortOrder, router, searchParams]);

const handleSortChange = (field: SortField) => {
const newOrder =
field === sortField && sortOrder === "asc" ? "desc" : "asc";
setSortField(field);
setSortOrder(newOrder);
};

return (
<div className="mb-4 flex w-full animate-fade-down flex-col items-center justify-between gap-2 border-b border-white/20 pb-4 animate-delay-200 md:flex-row">
<span className="w-full text-white">Sort by:</span>
{(Object.keys(sortFieldLabels) as SortField[]).map((field) => (
<button
key={field}
onClick={() => handleSortChange(field)}
className={`w-full py-1 text-sm ${
sortField === field
? "border border-cyan-500 bg-cyan-500/20 text-white"
: "border border-white/20 bg-[#898989]/5 text-gray-300 hover:bg-gray-600/50"
}`}
>
{sortFieldLabels[field]}{" "}
{sortField === field && (sortOrder === "asc" ? "↑" : "↓")}
</button>
))}
</div>
);
}
45 changes: 45 additions & 0 deletions packages/api/src/router/subnet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,51 @@ export const subnetRouter = {
where: eq(subnetDataSchema.id, input.id),
});
}),
paginatedAll: publicProcedure
.input(
z.object({
page: z.number().int().positive().default(1),
limit: z.number().int().positive().max(100).default(50),
sortBy: z
.enum([
"id",
"founderShare",
"incentiveRatio",
"proposalRewardTreasuryAllocation",
"minValidatorStake",
"createdAt",
])
.default("id"),
order: z.enum(["asc", "desc"]).default("asc"),
}),
)
.query(async ({ ctx, input }) => {
const { page, limit, sortBy, order } = input;
const offset = (page - 1) * limit;

const subnets = await ctx.db.query.subnetDataSchema.findMany({
limit: limit,
offset: offset,
orderBy: (subnetData, { asc, desc }) => [
order === "asc" ? asc(subnetData[sortBy]) : desc(subnetData[sortBy]),
],
});

const totalCount = await ctx.db
.select({ count: sql`count(*)` })
.from(subnetDataSchema)
.then((result) => Number(result[0]?.count));

return {
subnets,
metadata: {
currentPage: page,
pageSize: limit,
totalCount,
totalPages: Math.ceil(totalCount / limit),
},
};
}),
byUserSubnetData: publicProcedure
.input(z.object({ userKey: z.string() }))
.query(async ({ ctx, input }) => {
Expand Down
47 changes: 29 additions & 18 deletions packages/subspace/queries/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ import type {
} from "@commune-ts/utils";
import {
checkSS58,
STAKE_OUT_DATA_SCHEMA,
GOVERNANCE_CONFIG_SCHEMA,
isSS58,
STAKE_FROM_SCHEMA,
MODULE_BURN_CONFIG_SCHEMA,
NetworkSubnetConfigSchema,
STAKE_FROM_SCHEMA,
STAKE_OUT_DATA_SCHEMA,
SUBSPACE_MODULE_SCHEMA,
} from "@commune-ts/types";
import {
Expand All @@ -36,7 +36,6 @@ import {
standardizeUidToSS58address,
} from "@commune-ts/utils";


export { ApiPromise };

// == chain ==
Expand Down Expand Up @@ -348,7 +347,7 @@ export async function queryStakeOutCORRECT(
if (!response.ok) {
throw new Error("Failed to fetch data");
}
const stakeOutData = STAKE_OUT_DATA_SCHEMA.parse(await response.json())
const stakeOutData = STAKE_OUT_DATA_SCHEMA.parse(await response.json());
return stakeOutData;
}

Expand Down Expand Up @@ -521,8 +520,9 @@ export async function queryUserTotalStaked(
* @param netuidWhitelist if empty, modules from all subnets are returned
*/


export async function querySubnetParams(api: Api): Promise<NetworkSubnetConfig[]> {
export async function querySubnetParams(
api: Api,
): Promise<NetworkSubnetConfig[]> {
const subnetProps: SubspaceStorageName[] = [
"subnetNames",
"immunityPeriod",
Expand Down Expand Up @@ -550,7 +550,6 @@ export async function querySubnetParams(api: Api): Promise<NetworkSubnetConfig[]
const subnetInfo = await queryChain(api, props);
const subnetNames = subnetInfo.subnetNames;


const subnets: NetworkSubnetConfig[] = [];
for (const [netuid, _] of Object.entries(subnetNames)) {
const subnet: NetworkSubnetConfig = NetworkSubnetConfigSchema.parse({
Expand All @@ -566,13 +565,18 @@ export async function querySubnetParams(api: Api): Promise<NetworkSubnetConfig[]
trustRatio: subnetInfo.trustRatio[netuid]!,
maxWeightAge: subnetInfo.maxWeightAge[netuid]!,
bondsMovingAverage: subnetInfo.bondsMovingAverage[netuid],
maximumSetWeightCallsPerEpoch: subnetInfo.maximumSetWeightCallsPerEpoch[netuid],
maximumSetWeightCallsPerEpoch:
subnetInfo.maximumSetWeightCallsPerEpoch[netuid],
minValidatorStake: subnetInfo.minValidatorStake[netuid]!,
maxAllowedValidators: subnetInfo.maxAllowedValidators[netuid],
moduleBurnConfig: MODULE_BURN_CONFIG_SCHEMA.parse(subnetInfo.moduleBurnConfig[netuid]),
moduleBurnConfig: MODULE_BURN_CONFIG_SCHEMA.parse(
subnetInfo.moduleBurnConfig[netuid],
),
subnetMetadata: subnetInfo.subnetMetadata[netuid],
netuid: netuid,
subnetGovernanceConfig: GOVERNANCE_CONFIG_SCHEMA.parse(subnetInfo.subnetGovernanceConfig[netuid]),
subnetGovernanceConfig: GOVERNANCE_CONFIG_SCHEMA.parse(
subnetInfo.subnetGovernanceConfig[netuid],
),
subnetEmission: subnetInfo.subnetEmission[netuid],
});
subnets.push(subnet);
Expand All @@ -582,7 +586,7 @@ export async function querySubnetParams(api: Api): Promise<NetworkSubnetConfig[]

export function keyStakeFrom(
targetKey: SS58Address,
stakeFromStorage: Map<SS58Address, Map<SS58Address, bigint>>
stakeFromStorage: Map<SS58Address, Map<SS58Address, bigint>>,
) {
const stakerMap = stakeFromStorage.get(targetKey);
let totalStake = 0n;
Expand Down Expand Up @@ -618,13 +622,17 @@ export async function queryRegisteredModulesInfo(
"dividends",
"delegationFee",
"stakeFrom",
]
];

const extraPropsQuery: { subspaceModule: SubspaceStorageName[] } = { subspaceModule: moduleProps }
const extraPropsQuery: { subspaceModule: SubspaceStorageName[] } = {
subspaceModule: moduleProps,
};
const modulesInfo = await queryChain(api, extraPropsQuery, netuid);
const processedModules = standardizeUidToSS58address(modulesInfo, uidToSS58);
const moduleMap: SubspaceModule[] = [];
const parsedStakeFromStorage = STAKE_FROM_SCHEMA.parse({ stakeFromStorage: processedModules.stakeFrom });
const parsedStakeFromStorage = STAKE_FROM_SCHEMA.parse({
stakeFromStorage: processedModules.stakeFrom,
});

for (const uid of Object.keys(uidToSS58)) {
const moduleKey = uidToSS58[uid];
Expand All @@ -646,10 +654,13 @@ export async function queryRegisteredModulesInfo(
incentive: processedModules.incentive[moduleKey],
dividends: processedModules.dividends[moduleKey],
delegationFee: processedModules.delegationFee[moduleKey],
totalStaked: keyStakeFrom(moduleKey, parsedStakeFromStorage.stakeFromStorage),
totalStakers: parsedStakeFromStorage.stakeFromStorage.get(moduleKey)?.size ?? 0,

})
totalStaked: keyStakeFrom(
moduleKey,
parsedStakeFromStorage.stakeFromStorage,
),
totalStakers:
parsedStakeFromStorage.stakeFromStorage.get(moduleKey)?.size ?? 0,
});
moduleMap.push(module);
}
return moduleMap;
Expand Down

0 comments on commit 7147db1

Please sign in to comment.