diff --git a/packages/web-app/app/_lib/hooks.tsx b/packages/web-app/app/_lib/hooks.tsx index 85ffe087..a9981337 100644 --- a/packages/web-app/app/_lib/hooks.tsx +++ b/packages/web-app/app/_lib/hooks.tsx @@ -2,9 +2,11 @@ import { useEffect, useMemo, useState } from 'react'; import { useFetchCredentials, useFetchProjectsSaleDetails, + useFetchRisingTideCap, usePaymentTokenBalance, useProjectPublicInfo, usePublicInfo, + useTotalInvestedUsdcCtznd, } from './queries'; import { useKyc } from '../_providers/kyc/context'; import { compareAddresses, isValidGrant } from './utils'; @@ -14,6 +16,8 @@ import { useReadCtzndSalePaymentToken, useReadCtzndSalePaymentTokenToToken, useReadCtzndErc20Allowance, + useReadCtzndSaleMaxTarget, + useReadCtzndSaleMinTarget, } from '@/wagmi.generated'; import { formatEther, parseEther } from 'viem'; import { sepolia } from 'viem/chains'; @@ -246,3 +250,44 @@ export const useEffectSafe = (callback: () => void, deps: any[]) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, deps); }; + +export const useCtzndSaleCapStatus = () => { + const totalInvested = useTotalInvestedUsdcCtznd(); + const { data: maxTarget } = useReadCtzndSaleMaxTarget(); + const { data: minTarget } = useReadCtzndSaleMinTarget(); + + const investedValue = Number(totalInvested); + const maxValue = maxTarget ? Number(formatEther(maxTarget)) : undefined; + const minValue = minTarget ? Number(formatEther(minTarget)) : undefined; + + if (maxValue === undefined || minValue === undefined) { + return 'loading'; + } + + if (investedValue > maxValue) { + return 'above'; + } + + if (investedValue < minValue) { + return 'below'; + } + + return 'within'; +}; + +export const useCtzndRisingTideCap = () => { + const status = useCtzndSaleCapStatus(); + const aboveCap = status === 'above'; + const { data, isLoading, error } = useFetchRisingTideCap(aboveCap); + const cap = aboveCap && data ? formatEther(data) : 'N/A'; + + const result = useMemo(() => { + return { + data: cap, + isLoading, + error, + }; + }, [cap, isLoading, error]); + + return result; +}; diff --git a/packages/web-app/app/_lib/queries.tsx b/packages/web-app/app/_lib/queries.tsx index c5187763..73b6053b 100644 --- a/packages/web-app/app/_lib/queries.tsx +++ b/packages/web-app/app/_lib/queries.tsx @@ -21,6 +21,7 @@ import { useReadCtzndSaleUncappedAllocation, } from '@/wagmi.generated'; import { formatEther } from 'viem'; +import { computeRisingTideCap } from '../_server/risingTide/risingtide'; export const usePublicInfo = () => { return useQuery({ @@ -303,3 +304,16 @@ export const useCtzndMinContributionUsdc = () => { return usdcValue; }; + +export const useFetchRisingTideCap = (enabled?: boolean) => { + return useQuery({ + queryKey: ['rising-tide-cap'], + queryFn: async () => { + const cap = await computeRisingTideCap(); + + return cap; + }, + refetchInterval: 1000 * 1, // 1 minute + enabled, + }); +}; diff --git a/packages/web-app/app/_providers/idos/index.tsx b/packages/web-app/app/_providers/idos/index.tsx index 6894bcb9..23f64e8c 100644 --- a/packages/web-app/app/_providers/idos/index.tsx +++ b/packages/web-app/app/_providers/idos/index.tsx @@ -45,7 +45,7 @@ export const IdOsProvider = ({ children }: PropsWithChildren) => { // Authenticate by signing a message if (profile) { - // @ts-expect-error + // @ts-ignore await sdk.setSigner('EVM', ethSigner); setHasSigner(true); return; diff --git a/packages/web-app/app/_server/risingTide/risingtide.ts b/packages/web-app/app/_server/risingTide/risingtide.ts index 3dbc1189..654aff01 100644 --- a/packages/web-app/app/_server/risingTide/risingtide.ts +++ b/packages/web-app/app/_server/risingTide/risingtide.ts @@ -12,7 +12,7 @@ const client = createPublicClient({ transport: http(), }).extend(publicActions); -export const computeRisingTideCap = async () => { +export const computeRisingTideCap = async (): Promise => { const purchases = await client.getContractEvents({ address: saleContractAddress, abi: ctzndSaleAbi, diff --git a/packages/web-app/app/_ui/project/apply-button.tsx b/packages/web-app/app/_ui/project/apply-button.tsx index 345ff8aa..6f841308 100644 --- a/packages/web-app/app/_ui/project/apply-button.tsx +++ b/packages/web-app/app/_ui/project/apply-button.tsx @@ -39,7 +39,7 @@ export const ApplyButton = ({ isLoading, error }: TApplyButtonProps) => { return ( ); }; diff --git a/packages/web-app/app/_ui/project/citizend-project-description.tsx b/packages/web-app/app/_ui/project/citizend-project-description.tsx index bcb213d9..7ac6466f 100644 --- a/packages/web-app/app/_ui/project/citizend-project-description.tsx +++ b/packages/web-app/app/_ui/project/citizend-project-description.tsx @@ -1,30 +1,458 @@ +import Image from 'next/image'; +import fractal from '../../../public/fractal.png'; +import outlier from '../../../public/outlier.png'; + +const StepOne = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + +); + +const StepTwo = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + +); + +const StepThree = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + +); + +export const Arbitrum = () => ( + + + + + + + + + + + + + + + + +); + export const CitizendProjectDescription = () => { return (

Description

-

- Citizend provides a community-curated filter mechanism able to identify - high-quality projects, and aligning incentives between projects and web3 - users. +

+ Citizend provides a community-curated filter mechanism that is able to + identify high-quality projects at their earliest stages, and aligning + incentives between projects and web3 users enabling both Community Sales + and Public token launches. +

+
+

+ Citizend is not just another token launch platform; it is a movement + towards a more democratic, transparent, and community-focused web3 + ecosystem. By empowering its community to curate and select projects, + citizend is breaking new ground, fostering a space where innovation + thrives on the merits of consensus and shared values. Created by the + community, for the community.highest potential. +

+

How does it work

+

+ Projects hoping to secure community contributions or distribute tokens + on citizend must garner the greatest number of votes from the community + to win a slot, with each member of the community being able to cast only + one vote, among multiple competing projects in each batch. To do so, + projects must convince the community of their worth, as community + members only receive allocations if their chosen project is elected, + they are incentivized to research and select the projects with the + highest potential. +

+
+
+
+ +
+
+

Explore

+

+ Explore projects, sign up, and start your KYC process to prepare + for voting. +

+
+
+
+
+ +
+
+

Vote

+

+ Cast your single vote for your favorite project to help determine + the winner of the contribution slot. +

+
+
+
+
+ +
+
+

Contribute

+

+ Contribute to the winning project using the Two-pool contribution + system, ensuring fair allocation. +

+
+
+
+

Key partners and Backers

+

+ We built citizend to match great projects with web3-native verified + users. List of our partners and backers include Fractal ID, Outlier + Ventures, Arbitrum and more. +

+
+
+
+ fractal logo +
+
+

Fractal ID

+

+ Fractal ID is one of the leading KYC and compliance providers for + web3 with more than 1.1m users verified and 250 projects supported + since 2017. Fractal ID develops decentralized identity solutions + for dApps and ecosystems to ensure a safer, privacy-preserving + web3. +

+

+1.1m users verified

+
+
+
+
+ fractal logo +
+
+

Outlier Ventures

+

+ Outlier Ventures, founded in 2014, is the largest web3-focused + accelerator, with more than 270 projects accelerated and a network + of 500+ investors and top VCs.{' '} +

+

+140 projects every year

+
+
+
+
+ +
+
+

Arbitrum

+

+ Arbitrum is the leading Layer 2 ecosystem for scaling and + empowering Ethereum with more than +600k weekly active addresses + and +800 projects deployed and a strong focus on DeFi. +

+

+15bn Total Value Locked

+
+
+
+

The rising tide mechanism

+

+ A standout feature of citizend’s approach is the Rising Tide Mechanism, + a cleverly designed system that optimizes for community distribution, + while enabling price discovery. This mechanism ensures that resources + are allocated fairly among contributors, facilitating a transparent and + equitable funding process. It’s a testament to citizend’s commitment to + fairness, ensuring that all community members, regardless of their + contribution size, have an opportunity to participate in the success of + the projects they support.


- Projects hoping to distribute tokens on citizend must garner the - greatest number of votes from the community to win a slot, with each - member of the community being able to cast only one vote, among multiple - competing projects in each batch. To do so, projects must convince the - community of their worth, as community members only receive allocations - if their chosen project is elected, they are incentivized to research - and select the projects with the highest potential. + The Rising Tide Mechanism embodies citizend’s vision of a token launch + platform that values community engagement and contribution equally, + paving the way for a more inclusive and balanced web3 funding ecosystem.

-

Key partners

+

Privacy and Compliance with idOS

- Citizend is brought to you by Fractal ID and Outlier Ventures, with - additional supporting partners like Subvisual and Unique Network. We aim - to build a token funding platform that complies with all regulatory - standards, while we ensure people are in control of their data. We walk - the talk by progressively decentralizing the project from the start and - allowing everyone to participate. + Citizend places a strong emphasis on ensuring the confidentiality and + security of its users’ information. Leveraging idOS, a state-of-the-art + solution for identity verification, citizend enables users to manage + their credentials in a self-sovereign, privacy-preserving manner. This + not only improves user trust but also aligns with the broader web3 + vision of decentralization and user control.

); diff --git a/packages/web-app/app/_ui/project/project-content.tsx b/packages/web-app/app/_ui/project/project-content.tsx index 5b8fd5b1..68385e8f 100644 --- a/packages/web-app/app/_ui/project/project-content.tsx +++ b/packages/web-app/app/_ui/project/project-content.tsx @@ -86,15 +86,16 @@ export const ProjectContent = () => { applied={!!hasGrant} /> ) : null} - - {hasGrant && process.env.NEXT_PUBLIC_APPLY_OPEN === 'true' ? ( - - ) : null} - {process.env.NEXT_PUBLIC_APPLY_OPEN === 'true' && !hasGrant ? ( - + + {process.env.NEXT_PUBLIC_APPLY_OPEN === 'true' ? ( + hasGrant ? ( + + ) : ( + + ) ) : null} @@ -118,12 +119,23 @@ export const ProjectContent = () => { applied={!!hasGrant} /> ) : null} - - {hasGrant && process.env.NEXT_PUBLIC_APPLY_OPEN === 'true' ? ( - + + {process.env.NEXT_PUBLIC_APPLY_OPEN === 'true' ? ( + hasGrant ? ( + + ) : ( + + ) ) : null} - {process.env.NEXT_PUBLIC_APPLY_OPEN === 'true' && !hasGrant ? ( - + {process.env.NEXT_PUBLIC_CONTRIBUTE_OPEN === 'true' && !hasGrant ? ( +
+ This token sale is exclusively open to individuals who have + previously applied for participation. Stay tuned for future + opportunities. +
) : null} diff --git a/packages/web-app/app/_ui/project/token-metrics.tsx b/packages/web-app/app/_ui/project/token-metrics.tsx index 2ed74733..255b0a44 100644 --- a/packages/web-app/app/_ui/project/token-metrics.tsx +++ b/packages/web-app/app/_ui/project/token-metrics.tsx @@ -7,6 +7,8 @@ import { formatEther } from 'viem'; import clsx from 'clsx'; import { number } from '../utils/intl-formaters/number'; import { useTotalInvestedUsdcCtznd } from '@/app/_lib/queries'; +import { useCtzndRisingTideCap, useCtzndSaleCapStatus } from '@/app/_lib/hooks'; +import Link from 'next/link'; const ProgressBar = ({ title, @@ -17,8 +19,8 @@ const ProgressBar = ({ max: number; value: number; }) => { - const valueInMillions = value / 1000_000; - const maxInMillions = max / 1000_000; + const valueInMillions = value / 1_000_000; + const maxInMillions = max / 1_000_000; const halfInMillions = maxInMillions / 2; const currentRelativeValue = valueInMillions / maxInMillions; const percentage = currentRelativeValue * 100; @@ -85,7 +87,47 @@ const LoadingField = () => (
); -export const TokenMetrics = () => { +const Info = () => { + const status = useCtzndSaleCapStatus(); + + if (status === 'below') { + return ( +
+ *If the total contributions fall below $500K, the token will not be + launched, and refunds will be issued. +
+ ); + } + + if (status === 'within') { + return ( +
+ *At this contribution level you will receive your full desired + contribution. +
+ ); + } + + if (status === 'above') { + return ( +
+ *The contributions exceed $1M, activating our Rising Tide Mechanism. + Your final token allocation will be determined by the number of + participants and their total contribution amount. + + Learn more about the Rising Tide Mechanism. + +
+ ); + } +}; + +export const TokenMetrics = ({ hasGrant }: { hasGrant: boolean }) => { const { data: maxTarget } = useReadCtzndSaleMaxTarget(); const totalCommitted = useTotalInvestedUsdcCtznd(); const maxValue = maxTarget ? Number(formatEther(maxTarget)) : 0; @@ -94,11 +136,21 @@ export const TokenMetrics = () => { refetchInterval: 1000 * 10, // 10 seconds }, }); + const { data: cap, isLoading: isLoadingCap } = useCtzndRisingTideCap(); return (
-

+

Community Sale Status + {process.env.NEXT_PUBLIC_CONTRIBUTE_OPEN === 'true' ? ( +
+ Live + + + + +
+ ) : null}

{process.env.NEXT_PUBLIC_CONTRIBUTE_OPEN === 'true' ? (
@@ -141,9 +193,12 @@ export const TokenMetrics = () => { Current max. allocation/participant: - {'N/A'} + + {isLoadingCap ? : cap} +
+ {hasGrant ? : null}
); }; diff --git a/packages/web-app/public/fractal.png b/packages/web-app/public/fractal.png new file mode 100644 index 00000000..06abea71 Binary files /dev/null and b/packages/web-app/public/fractal.png differ diff --git a/packages/web-app/public/outlier.png b/packages/web-app/public/outlier.png new file mode 100644 index 00000000..ac273f49 Binary files /dev/null and b/packages/web-app/public/outlier.png differ