Skip to content

Commit

Permalink
Merge pull request #3 from jmrossy/tx-forms
Browse files Browse the repository at this point in the history
Register & lock transaction forms
  • Loading branch information
jmrossy authored Jan 20, 2024
2 parents a3c0a8d + 07fed32 commit ea00a81
Show file tree
Hide file tree
Showing 68 changed files with 1,365 additions and 641 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"react-minimal-pie-chart": "^8.4.0",
"react-toastify": "^9.1.3",
"viem": "2.0.6",
"wagmi": "2.2.0"
"wagmi": "2.2.1"
},
"devDependencies": {
"@tanstack/eslint-plugin-query": "^5.17.7",
Expand Down
62 changes: 60 additions & 2 deletions src/app/account/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,72 @@
'use client';

import Image from 'next/image';
import { SolidButton } from 'src/components/buttons/SolidButton';
import { Section } from 'src/components/layout/Section';
import { Amount } from 'src/components/numbers/Amount';
import { useBalance, useLockedBalance } from 'src/features/account/hooks';
import { LockActionType } from 'src/features/locking/types';
import { useStakingRewards } from 'src/features/staking/rewards/useStakingRewards';
import { useStakingBalances } from 'src/features/staking/useStakingBalances';
import { useTransactionModal } from 'src/features/transactions/TransactionModal';
import { TxModalType } from 'src/features/transactions/types';
import Lock from 'src/images/icons/lock.svg';
import Unlock from 'src/images/icons/unlock.svg';
import Withdraw from 'src/images/icons/withdraw.svg';
import { usePageInvariant } from 'src/utils/navigation';
import { useAccount } from 'wagmi';

export default function Page() {
const account = useAccount();
const address = account?.address;

usePageInvariant(!!address, '/', 'No account connected');

return <Section containerClassName="space-y-8">TODO</Section>;
const { balance: walletBalance } = useBalance(address);
const { lockedBalance } = useLockedBalance(address);
const { groupToStake } = useStakingBalances(address);
const { totalRewards: _totalRewards } = useStakingRewards(address, groupToStake);

const totalBalance = (walletBalance || 0n) + (lockedBalance || 0n);

const showTxModal = useTransactionModal();

return (
<Section className="mt-6" containerClassName="space-y-8 min-w-[40rem]">
<h1 className="font-serif text-3xl">Dashboard</h1>
<div className="flex items-center justify-between">
<div>
<h2>Total Balance</h2>
<Amount valueWei={totalBalance} className="mt-2 text-4xl" />
</div>
<div className="flex space-x-2">
<SolidButton
onClick={() => showTxModal(TxModalType.Lock, { defaultAction: LockActionType.Lock })}
>
<div className="flex items-center space-x-1.5">
<Image src={Lock} width={12} height={12} alt="" />
<span>Lock</span>
</div>
</SolidButton>
<SolidButton
onClick={() => showTxModal(TxModalType.Lock, { defaultAction: LockActionType.Unlock })}
>
<div className="flex items-center space-x-1.5">
<Image src={Unlock} width={12} height={12} alt="" />
<span>Unlock</span>
</div>
</SolidButton>
<SolidButton
onClick={() =>
showTxModal(TxModalType.Lock, { defaultAction: LockActionType.Withdraw })
}
>
<div className="flex items-center space-x-1.5">
<Image src={Withdraw} width={12} height={12} alt="" />
<span>Withdraw</span>
</div>
</SolidButton>
</div>
</div>
</Section>
);
}
104 changes: 46 additions & 58 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,98 +1,86 @@
'use client';
import { useMemo } from 'react';
import { SolidButton } from 'src/components/buttons/SolidButton';
import { Card } from 'src/components/layout/Card';
import { Section } from 'src/components/layout/Section';
import { Modal, useModal } from 'src/components/menus/Modal';
import { Amount } from 'src/components/numbers/Amount';
import { StakeForm } from 'src/features/staking/StakeForm';
import { StatBox } from 'src/components/layout/StatBox';
import { useTransactionModal } from 'src/features/transactions/TransactionModal';
import { TxModalType } from 'src/features/transactions/types';
import { ValidatorGroupTable } from 'src/features/validators/ValidatorGroupTable';
import { ValidatorGroup, ValidatorStatus } from 'src/features/validators/types';
import { useValidatorGroups } from 'src/features/validators/useValidatorGroups';
import { bigIntMin } from 'src/utils/math';
import { objLength } from 'src/utils/objects';

export default function Index() {
const { groups, totalVotes } = useValidatorGroups();

const { isModalOpen, openModal: _openModal, closeModal } = useModal();

return (
<>
<div className="space-y-8">
<HeroSection totalVotes={totalVotes} groups={groups} />
<ListSection totalVotes={totalVotes} groups={groups} />
</div>
<Modal isOpen={isModalOpen} close={closeModal}>
<StakeForm />
</Modal>
<Section className="mt-6">
<div className="space-y-8">
<HeroSection totalVotes={totalVotes} groups={groups} />
<ListSection totalVotes={totalVotes} groups={groups} />
</div>
</Section>
</>
);
}

function HeroSection({ totalVotes, groups }: { totalVotes?: bigint; groups?: ValidatorGroup[] }) {
const minVotes = useMemo(() => {
if (!groups?.length) return 0n;
let min = BigInt(1e40);
const { minVotes, numValidators } = useMemo(() => {
if (!groups?.length) return { minVotes: 0n, numValidators: 0 };
let minVotes = BigInt(1e40);
let numValidators = 0;
for (const g of groups) {
numValidators += objLength(g.members);
const numElectedMembers = Object.values(g.members).filter(
(m) => m.status === ValidatorStatus.Elected,
).length;
if (!numElectedMembers) continue;
const votesPerMember = g.votes / BigInt(numElectedMembers);
min = bigIntMin(min, votesPerMember);
minVotes = bigIntMin(minVotes, votesPerMember);
}
return min;
return { minVotes, numValidators };
}, [groups]);

const showStakeModal = useTransactionModal(TxModalType.Stake);

return (
<Section className="bg-purple-500 text-white" containerClassName="all:px-0">
<div className="my-10 flex items-center justify-between gap-20 lg:gap-x-40 xl:gap-x-80">
<div className="flex w-80 flex-col space-y-6">
<h1 className="font-serif text-4xl">Discover Validators</h1>
<p>Stake your CELO with validators to start earning rewards immediately.</p>
<SolidButton onClick={showStakeModal}>{`Stake and earn 4%`}</SolidButton>
</div>
<div className="hidden grid-cols-2 grid-rows-2 gap-10 border border-white/20 p-6 md:grid">
<HeroStat label="Staking APY" text="6%" />
<HeroStat label="Validators Groups" text={groups?.length || 0} />
<HeroStat label="Elected Minimum Votes" amount={minVotes} />
<HeroStat label="Total Staked CELO" amount={totalVotes} />
</div>
<div className="space-y-4">
<div className="flex items-center justify-between">
<h1 className="font-serif text-4xl">Discover Validators</h1>
<SolidButton onClick={() => showStakeModal()} className="px-8">
Stake
</SolidButton>
</div>
<div className="flex items-center justify-between gap-2 sm:gap-8">
<StatBox
header="Total Staked CELO"
valueWei={totalVotes}
className="max-w-xs md:max-w-sm"
/>
<StatBox
header="Elected Minimum Votes"
valueWei={minVotes}
className="max-w-xs md:max-w-sm"
/>
<StatBox className="hidden max-w-xs md:max-w-sm lg:flex">
<div className="flex gap-6 divide-x">
<div className="flex flex-col">
<h3 className="text-sm">Total Groups</h3>
<div className="mt-2 font-serif text-xl md:text-2xl">{groups?.length || 0}</div>
</div>
<div className="flex flex-col pl-6">
<h3 className="text-sm">Total Validators</h3>
<div className="mt-2 font-serif text-xl md:text-2xl">{numValidators}</div>
</div>
</div>
</StatBox>
</div>
</Section>
);
}

function HeroStat({
label,
text,
amount,
}: {
label: string;
text?: string | number;
amount?: bigint;
}) {
return (
<div className="flex flex-col">
<label>{label}</label>
{!!text && <div className="mt-1 font-serif text-3xl">{text}</div>}
{!!amount && (
<Amount valueWei={amount} className="mt-1 text-3xl" decimals={0} showSymbol={false} />
)}
</div>
);
}

function ListSection({ totalVotes, groups }: { totalVotes?: bigint; groups?: ValidatorGroup[] }) {
return (
<Section containerClassName="all:px-0">
<Card className="p-0" bodyClassName="p-0">
<ValidatorGroupTable groups={groups || []} totalVotes={totalVotes || 0n} />
</Card>
</Section>
);
return <ValidatorGroupTable groups={groups || []} totalVotes={totalVotes || 0n} />;
}
Loading

0 comments on commit ea00a81

Please sign in to comment.