Skip to content

Commit

Permalink
Read project data from contracts (#219)
Browse files Browse the repository at this point in the history
Why:
* We had hardcoded values that needed to be replaced by the real values

Also:
- Adds functional allowance and contribution

---------

Co-authored-by: Luís Torres <[email protected]>
Co-authored-by: Luís Torres <[email protected]>
  • Loading branch information
3 people authored Apr 23, 2024
1 parent 5094602 commit 918355b
Show file tree
Hide file tree
Showing 6 changed files with 268 additions and 104 deletions.
87 changes: 85 additions & 2 deletions packages/web-app/app/_lib/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useIdOS } from '../_providers/idos';
import { insertGrantBySignature } from '../_server/idos/grants';
import { useFetchGrantMessage } from './contract-queries';
import { useSignMessage } from 'wagmi';
import { useCallback, useEffect, useMemo } from 'react';
import { useAccount, useSignMessage } from 'wagmi';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useFetchNewDataId } from './queries';
import { getServerPublicInfo } from '../_server/info';
import { subscribeToNewsletter } from '../_server/active-campaign';
import {
ctzndSaleAddress,
useReadCtzndSalePaymentToken,
useReadCtzndSalePaymentTokenToToken,
useReadErc20Allowance,
useWriteCtzndSaleBuy,
useWriteErc20Approve,
} from '@/wagmi.generated';
import { sepolia } from 'viem/chains';
import { formatEther, parseEther } from 'viem';

export const useAcquireAccessGrantMutation = () => {
const { sdk } = useIdOS();
Expand Down Expand Up @@ -192,3 +202,76 @@ export const useSignDelegatedAccessGrant = (
insertError,
};
};

export const useContributeToCtznd = (address: `0x${string}`) => {
const [state, setState] = useState();
const [amount, setAmount] = useState(0);
const amountInWei = useMemo(() => parseEther(amount.toString()), [amount]);

const {
writeContract: writeBuy,
data: contributionTxHash,
isPending: isWritePending,
error: buyError,
} = useWriteCtzndSaleBuy();
const {
writeContract: approveContract,
data: allowanceTxHash,
error: allowanceError,
isPending: isApprovePending,
} = useWriteErc20Approve();
const { data: paymentToken } = useReadCtzndSalePaymentToken();
const saleAddress = ctzndSaleAddress[sepolia.id];
const { data: allowance } = useReadErc20Allowance({
address: paymentToken,
args: [address, saleAddress],
query: {
refetchInterval: 1000,
},
});
const { data: tokensToBuy, error: tokenError } =
useReadCtzndSalePaymentTokenToToken({
args: [amountInWei],
});

const diffToAllowance = useMemo(
() => (allowance || BigInt(0)) - amountInWei,
[allowance, amountInWei],
);

const submit = () => {
if (
amount <= 0 ||
allowance === undefined ||
paymentToken === undefined ||
tokensToBuy === undefined
) {
return;
}

if (diffToAllowance < 0) {
return approveContract({
address: paymentToken,
args: [saleAddress, amountInWei],
});
}

writeBuy({ args: [tokensToBuy] });
};

return {
contributionTxHash,
allowanceTxHash,
diffToAllowance,
amount,
amountInWei,
setAmount,
tokensToBuy,
isPending: isApprovePending || isWritePending,
error: buyError || allowanceError || tokenError,
buyError,
allowanceError,
tokenError,
submit,
};
};
6 changes: 5 additions & 1 deletion packages/web-app/app/_ui/my-projects/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@ import Image from 'next/image';
import { getRelativePath } from '../utils/getRelativePath';
import { NoProjects } from './no-projects';
import { MyProjectSkeleton } from './my-project';
import { useReadCtzndSaleInvestorCount } from '@/wagmi.generated';

const ProjectRow = ({
logo,
project,
minTarget,
maxTarget,
}: TProjectSaleDetails) => {
const { data: contributions } = useReadCtzndSaleInvestorCount();
const totalContributions = contributions ? contributions.toString() : 0;

return (
<Link
href={`/my-projects/${project}`}
Expand All @@ -30,7 +34,7 @@ const ProjectRow = ({
/>
{project}
</div>
<div className="hidden md:block">0</div>
<div className="hidden md:block">{totalContributions}</div>
<div className="hidden md:block">{usdRange(minTarget, maxTarget)}</div>
<div>0 USDC</div>
</div>
Expand Down
21 changes: 19 additions & 2 deletions packages/web-app/app/_ui/my-projects/my-project.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,22 @@ import { usdRange } from '../utils/intl-formaters/usd-range';
import { formatDate } from '../utils/intl-formaters/date';
import { EdgeLink } from '../components/edge';
import { CardSkeleton } from '../components/skeletons/card-skeleton';
import {
useReadCtzndSaleInvestorCount,
useReadCtzndSaleUncappedAllocation,
} from '@/wagmi.generated';
import { useAccount } from 'wagmi';
import { formatEther } from 'viem';

const Header = ({
project,
logo,
minTarget,
maxTarget,
}: TProjectSaleDetails) => {
const { data: contributions } = useReadCtzndSaleInvestorCount();
const totalContributions = contributions ? contributions.toString() : 0;

return (
<div className="flex flex-col rounded-md bg-mono-50 px-6 py-8 text-mono-950">
<h2 className="flex items-center gap-4 text-2xl">
Expand All @@ -30,7 +39,7 @@ const Header = ({
<div className="grid grid-cols-1 gap-6 pt-6 md:grid-cols-3 md:px-14 md:pt-8">
<div className="flex flex-col gap-2 md:border-r md:border-mono-200">
<h3 className="text-sm text-mono-800">Contributions</h3>
<div>0</div>
<div>{totalContributions}</div>
</div>
<div className="flex flex-col gap-2 md:border-r md:border-mono-200">
<h3 className="text-sm text-mono-800">Target Raise</h3>
Expand All @@ -47,10 +56,18 @@ const Header = ({

const MyContribution = () => {
const { projectId } = useProject();

const { address } = useAccount();
const { data: contributions } = useReadCtzndSaleUncappedAllocation({
args: [address!],
});

const myContributions = contributions ? formatEther(contributions!) : 0;

return (
<div className="flex flex-col gap-2 rounded-md bg-mono-50 px-6 py-8 text-mono-950">
<h3 className="text-sm text-mono-800">Contributions</h3>
<div className="text-3.5xl">0 USDC</div>
<div className="text-3.5xl">{`${myContributions} USDC`}</div>
<div className="self-center pt-6">
<EdgeLink href={`/projects/${projectId}`}>New Contribution</EdgeLink>
</div>
Expand Down
96 changes: 96 additions & 0 deletions packages/web-app/app/_ui/project/contribution/DataFields.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import {
useReadCtzndSaleMaxContribution,
useReadCtzndSaleMinContribution,
useReadCtzndSaleInvestorCount,
useReadCtzndSaleMaxTarget,
useReadCtzndSaleMinTarget,
} from '@/wagmi.generated';
import { formatEther } from 'viem';
import { usdValue } from '../../utils/intl-formaters/usd-value';
import { number } from '../../utils/intl-formaters/number';

const useMaxParticipants = () => {
const {
data: maxTarget,
isLoading: maxLoading,
error: maxError,
} = useReadCtzndSaleMaxTarget();
const {
data: minTarget,
isLoading: minLoading,
error: minError,
} = useReadCtzndSaleMinTarget();

return {
data:
maxTarget === undefined || minTarget === undefined
? undefined
: BigInt(maxTarget) - BigInt(minTarget),
isLoading: maxLoading || minLoading,
error: maxError || minError,
};
};

const LoadingField = () => (
<div className="h-5 w-full animate-pulse rounded-md bg-gradient-to-br from-mono-50 to-mono-200" />
);

export const DataFields = () => {
const { data: maxContribution } = useReadCtzndSaleMaxContribution();
const { data: minContribution } = useReadCtzndSaleMinContribution();
const { data: investorCount } = useReadCtzndSaleInvestorCount({
query: {
refetchInterval: 1000 * 60, // every minute
},
});
const { data: maxParticipants } = useMaxParticipants();

return (
<>
<div className="md:col-span-2">
<div className="text-mono-800">Current price</div>
<div>0.1 USDC*</div>
</div>
<div>
<div className="text-mono-800">Min. contribution</div>
<div>
{minContribution !== undefined ? (
usdValue(formatEther(minContribution))
) : (
<LoadingField />
)}
</div>
</div>
<div>
<div className="text-mono-800">Max. contribution</div>
<div>
{maxContribution !== undefined ? (
usdValue(formatEther(maxContribution))
) : (
<LoadingField />
)}
</div>
</div>
<div>
<div className="text-mono-800">Current contributors</div>
<div>
{investorCount !== undefined ? (
number(investorCount)
) : (
<LoadingField />
)}
</div>
</div>
<div>
<div className="text-mono-800">Max. participants</div>
<div>
{maxParticipants !== undefined ? (
number(maxParticipants)
) : (
<LoadingField />
)}
</div>
</div>
</>
);
};
20 changes: 14 additions & 6 deletions packages/web-app/app/_ui/project/project-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ProjectContribution } from './project-contribution';
import { useHasCitizendGrant, useHasProjectGrant } from '@/app/_lib/hooks';
import { AppliedSuccess } from './applied-success';
import { CardSkeleton } from '../components/skeletons/card-skeleton';
import { useAccount } from 'wagmi';

const generateTabClassName = ({ selected }: { selected: boolean }) =>
clsx(
Expand All @@ -20,6 +21,7 @@ const generateTabClassName = ({ selected }: { selected: boolean }) =>
);

export const ProjectContent = () => {
const { address } = useAccount();
const { projectId } = useProject();
const [selectedIndex, setSelectedIndex] = useState(0);
const { data, isLoading, isError, error } = useFetchProjectsSaleDetails();
Expand All @@ -34,7 +36,7 @@ export const ProjectContent = () => {
);
const hasGrant = hasProjectGrant && hasCitizendGrant;

if (isLoading || (!data && !isError)) {
if (isLoading || isLoadingGrant || (!data && !isError)) {
return (
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
<CardSkeleton className="h-[544px]" />
Expand Down Expand Up @@ -80,8 +82,10 @@ export const ProjectContent = () => {
<CitizendProjectDescription />
</Tab.Panel>
<Tab.Panel className="flex flex-col gap-8 focus:outline-none">
{process.env.NEXT_PUBLIC_CONTRIBUTE_OPEN === 'true' ? (
<ProjectContribution />
{process.env.NEXT_PUBLIC_CONTRIBUTE_OPEN === 'true' &&
hasGrant &&
address ? (
<ProjectContribution userAddress={address} />
) : null}
{process.env.NEXT_PUBLIC_APPLY_OPEN === 'true' ? (
<ProjectInformation
Expand All @@ -98,7 +102,9 @@ export const ProjectContent = () => {
maxContribution={maxContribution}
totalTokensForSale={totalTokensForSale}
/>
{hasGrant ? <AppliedSuccess /> : null}
{hasGrant && process.env.NEXT_PUBLIC_APPLY_OPEN === 'true' ? (
<AppliedSuccess />
) : null}
{process.env.NEXT_PUBLIC_APPLY_OPEN === 'true' && !hasGrant ? (
<ApplyButton
isLoading={isLoadingGrant}
Expand All @@ -111,8 +117,10 @@ export const ProjectContent = () => {
</div>
<div className="hidden grid-cols-2 gap-x-8 md:grid">
<div className="flex flex-col gap-16">
{process.env.NEXT_PUBLIC_CONTRIBUTE_OPEN === 'true' ? (
<ProjectContribution />
{process.env.NEXT_PUBLIC_CONTRIBUTE_OPEN === 'true' &&
hasGrant &&
address ? (
<ProjectContribution userAddress={address} />
) : null}
<CitizendProjectDescription />
</div>
Expand Down
Loading

0 comments on commit 918355b

Please sign in to comment.