Skip to content

Commit

Permalink
Namadillo: Phase 2 - Claim Rewards + useTransaction (#1100)
Browse files Browse the repository at this point in the history
* feat(namadillo): creating useBalances hook

* feat(namadillo): separating colors and other props from tailwind config

* feat(components): adding label property to PieChart

* feat: refactoring balance props to be reused between staking and overview

* feat(namadillo): fixing overview chart

* feat(namadillo): adding mocks for useBalance

* feat(namadillo): adding tests for NamBalanceContainer

* feat(namadillo): adding footer to AccountOverview

* feat(namadillo): replacing by Panel component

* feat(namadillo): claim rewards component

* feat(namadillo): enabling staking rewards

* temp: claiming rewards wip

* feat(namadillo): adding useTransaction hook and refactoring claim rewards

* refactor(namadillo): applying useTransaction to IncrementBonding

* feat(namadillo): adding useTransaction on Unstake flow

* feat(namadillo): applying useTransaction to redelegate

* chore: fixing some merge conflicts

* feat(namadillo): applying useTransaction to Withdraw

* feat(namadillo): writing tests for StakingRewardsPanel

* feat(namadillo): adding claim and stake

* feat(namadillo): writing tests for StakingRewards

* fix(namadillo): fixing staking rewards not updating
  • Loading branch information
pedrorezende authored Sep 14, 2024
1 parent 94b6ec6 commit 89b2ece
Show file tree
Hide file tree
Showing 42 changed files with 1,562 additions and 634 deletions.
6 changes: 6 additions & 0 deletions apps/extension/src/Approvals/Commitment.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { TxType } from "@heliax/namada-sdk/web";
import {
BondProps,
ClaimRewardsProps,
CommitmentDetailProps,
RedelegateProps,
RevealPkProps,
Expand Down Expand Up @@ -103,7 +104,12 @@ const renderContent = (tx: CommitmentDetailProps): ReactNode => {
<>Reveal public key for address {formatAddress(revealTx.publicKey)}</>
);

case TxType.ClaimRewards:
const claimTx = tx as ClaimRewardsProps;
return <>Claiming rewards from {formatAddress(claimTx.validator)}</>;

// TODO: continue implementing other types in the next phases

default:
return <></>;
}
Expand Down
10 changes: 7 additions & 3 deletions apps/namadillo/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ module.exports = {
modulePathIgnorePatterns: ["e2e-tests"],
moduleDirectories: ["src", "node_modules"],
verbose: true,
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {
prefix: "<rootDir>/src/",
}),
moduleNameMapper: {
...pathsToModuleNameMapper(compilerOptions.paths, {
prefix: "<rootDir>/src/",
}),
"^.+\\.svg$": "jest-transformer-svg",
"\\.css": "identity-obj-proxy",
},
setupFilesAfterEnv: ["<rootDir>/src/setupTests.ts"],
};
3 changes: 3 additions & 0 deletions apps/namadillo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"test:watch": "yarn wasm:build:test && yarn jest --watchAll=true",
"test:coverage": "yarn wasm:build:test && yarn test --coverage",
"test:ci": "jest",
"test:watch-only": "yarn jest --watchAll=true",
"e2e-test": "PLAYWRIGHT_BASE_URL=http://localhost:3000 yarn playwright test",
"e2e-test:headed": "PLAYWRIGHT_BASE_URL=http://localhost:3000 yarn playwright test --project=chromium --headed",
"wasm:build": "node ./scripts/build.js --release",
Expand Down Expand Up @@ -106,10 +107,12 @@
"eslint-plugin-react-hooks": "^4.6.0",
"globals": "^15.9.0",
"history": "^5.3.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^29.7.0",
"jest-create-mock-instance": "^2.0.0",
"jest-environment-jsdom": "^29.7.0",
"jest-fetch-mock": "^3.0.3",
"jest-transformer-svg": "^2.0.2",
"local-cors-proxy": "^1.1.0",
"postcss": "^8.4.32",
"release-it": "^17.0.1",
Expand Down
59 changes: 43 additions & 16 deletions apps/namadillo/src/App/AccountOverview/AccountOverview.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,61 @@
import { Stack } from "@namada/components";
import { Panel, Stack } from "@namada/components";
import { Intro } from "App/Common/Intro";
import { PageWithSidebar } from "App/Common/PageWithSidebar";
import MainnetRoadmap from "App/Sidebars/MainnetRoadmap";
import { namadaExtensionConnectedAtom } from "atoms/settings";
import { StakingRewardsPanel } from "App/Staking/StakingRewardsPanel";
import {
applicationFeaturesAtom,
namadaExtensionConnectedAtom,
} from "atoms/settings";
import clsx from "clsx";
import { useAtomValue } from "jotai";
import { AccountBalanceContainer } from "./AccountBalanceContainer";
import { NamBalanceContainer } from "./NamBalanceContainer";
import { NavigationFooter } from "./NavigationFooter";

export const AccountOverview = (): JSX.Element => {
const isConnected = useAtomValue(namadaExtensionConnectedAtom);
const { claimRewardsEnabled, maspEnabled } = useAtomValue(
applicationFeaturesAtom
);

const fullView = claimRewardsEnabled || maspEnabled;
const fullWidthClassName = clsx({ "col-span-2": !isConnected });

return (
<PageWithSidebar>
<div
className={clsx(
"flex items-center bg-black rounded-sm w-full",
fullWidthClassName
)}
>
<div className={clsx("flex w-full", fullWidthClassName)}>
{!isConnected && (
<div className="w-[420px] mx-auto">
<Intro />
</div>
<section className="flex rounded-sm items-center w-full bg-black">
<div className="w-[420px] mx-auto">
<Intro />
</div>
</section>
)}
{isConnected && (
<Stack gap={5} className="my-auto min-w-[365px] mx-auto py-12">
<AccountBalanceContainer />
<NavigationFooter />
</Stack>

{isConnected && !fullView && (
<section className="flex items-center bg-black rounded-sm w-full">
<Stack gap={5} className="my-auto min-w-[365px] mx-auto py-12">
<AccountBalanceContainer />
<NavigationFooter />
</Stack>
</section>
)}

{isConnected && fullView && (
<section className="flex flex-col w-full rounded-sm min-h-full gap-2">
<div className="grid grid-cols-[1.25fr_1fr] gap-2">
<Panel className="pl-4 pr-6 py-5">
<NamBalanceContainer />
</Panel>
<Panel>
<StakingRewardsPanel />
</Panel>
</div>
<Panel className="flex items-center flex-1 justify-center">
<NavigationFooter />
</Panel>
</section>
)}
</div>
{isConnected && (
Expand Down
91 changes: 91 additions & 0 deletions apps/namadillo/src/App/AccountOverview/NamBalanceContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { Stack } from "@namada/components";
import { AtomErrorBoundary } from "App/Common/AtomErrorBoundary";
import { BalanceChart } from "App/Common/BalanceChart";
import { NamCurrency } from "App/Common/NamCurrency";
import BigNumber from "bignumber.js";
import { useBalances } from "hooks/useBalances";
import { colors } from "theme";

type NamBalanceListItemProps = {
title: string;
color: string;
amount: BigNumber;
};

const NamBalanceListItem = ({
title,
color,
amount,
}: NamBalanceListItemProps): JSX.Element => {
return (
<li
className="leading-5 bg-neutral-900 px-4 py-3 rounded-sm min-w-[165px]"
aria-description={`${title} amount is ${amount.toString()} NAM`}
>
<span className="flex items-center text-xs gap-1.5">
<i className="w-2 h-2 rounded-full" style={{ background: color }} />
{title}
</span>
<NamCurrency
amount={amount}
className="text-lg pl-3.5"
currencySignClassName="hidden"
/>
</li>
);
};

export const NamBalanceContainer = (): JSX.Element => {
const {
balanceQuery,
stakeQuery,
isLoading,
isSuccess,
availableAmount,
bondedAmount,
shieldedAmount,
unbondedAmount,
withdrawableAmount,
totalAmount,
} = useBalances();

return (
<div className="flex gap-4 text-white">
<AtomErrorBoundary
result={[balanceQuery, stakeQuery]}
niceError="Unable to load balances"
>
<div className="flex items-center w-full">
<BalanceChart
view="total"
isLoading={isLoading}
isSuccess={isSuccess}
availableAmount={availableAmount}
bondedAmount={bondedAmount}
shieldedAmount={shieldedAmount}
unbondedAmount={unbondedAmount}
withdrawableAmount={withdrawableAmount}
totalAmount={totalAmount}
/>
<Stack gap={2} as="ul">
<NamBalanceListItem
title="Available NAM"
color={colors.balance}
amount={availableAmount}
/>
<NamBalanceListItem
title="Staked NAM"
color={colors.bond}
amount={bondedAmount}
/>
<NamBalanceListItem
title="Unbonded NAM"
color={colors.unbond}
amount={unbondedAmount.plus(withdrawableAmount)}
/>
</Stack>
</div>
</AtomErrorBoundary>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { cleanup, render, screen } from "@testing-library/react";
import BigNumber from "bignumber.js";
import { mockUseBalances } from "hooks/__mocks__/mockUseBalance";
import { AtomWithQueryResult } from "jotai-tanstack-query";
import { NamBalanceContainer } from "../NamBalanceContainer";

jest.mock("hooks/useBalances", () => ({
useBalances: jest.fn(),
}));

describe("Component: NamBalanceContainer", () => {
beforeEach(() => {
jest.clearAllMocks();
});

test("renders error boundary when queries fail", () => {
// Check if the error message from AtomErrorBoundary is displayed
const execute = (
balanceQueryError: boolean,
stakeQueryError: boolean
): void => {
mockUseBalances({
balanceQuery: { isError: balanceQueryError } as AtomWithQueryResult,
stakeQuery: { isError: stakeQueryError } as AtomWithQueryResult,
isLoading: false,
isSuccess: false,
});
render(<NamBalanceContainer />);
expect(screen.getByText(/Unable to load balances/i)).toBeInTheDocument();
cleanup();
jest.clearAllMocks();
};

execute(true, true);
execute(false, true);
execute(true, false);
});

test("renders balance items when data is loaded", () => {
mockUseBalances({
availableAmount: new BigNumber(100),
bondedAmount: new BigNumber(50),
unbondedAmount: new BigNumber(30),
withdrawableAmount: new BigNumber(25),
totalAmount: new BigNumber(200),
});

render(<NamBalanceContainer />);

// Check if the list items for each balance type are rendered
expect(screen.getByText(/Available NAM/i)).toBeInTheDocument();
expect(screen.getByText(/Staked NAM/i)).toBeInTheDocument();
expect(screen.getByText(/Unbonded NAM/i)).toBeInTheDocument();

// Check if the amounts are displayed correctly

// Available:
expect(screen.getByText("100 NAM")).toBeInTheDocument();

// Bonded / Staked
expect(screen.getByText("50 NAM")).toBeInTheDocument();

// Unbonded + Withdraw
expect(screen.queryByText("30 NAM")).toBeNull();
expect(screen.queryByText("25 NAM")).toBeNull();
expect(screen.getByText("55 NAM")).toBeInTheDocument();
});
});
5 changes: 5 additions & 0 deletions apps/namadillo/src/App/AppRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import GovernanceRoutes from "./Governance/routes";
import SettingsRoutes from "./Settings/routes";
import { SignMessages } from "./SignMessages/SignMessages";
import MessageRoutes from "./SignMessages/routes";
import { StakingRewards } from "./Staking/StakingRewards";
import StakingRoutes from "./Staking/routes";
import SwitchAccountRoutes from "./SwitchAccount/routes";

Expand Down Expand Up @@ -60,6 +61,10 @@ export const MainRoutes = (): JSX.Element => {
element={<SignMessages />}
errorElement={<RouteErrorBoundary />}
/>
<Route
path={`${StakingRoutes.claimRewards().url}`}
element={<StakingRewards />}
/>
</Routes>
<ScrollRestoration />
</>
Expand Down
Loading

1 comment on commit 89b2ece

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.