From e48f2f923a403e006c4f9664fa4942febc5ab2e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Torres?= Date: Fri, 3 May 2024 11:52:16 +0100 Subject: [PATCH 1/5] Add rising tied cap value --- packages/web-app/app/_lib/hooks.tsx | 19 +++++++++++++++++ packages/web-app/app/_lib/queries.tsx | 14 +++++++++++++ .../web-app/app/_providers/idos/index.tsx | 2 +- .../app/_server/risingTide/risingtide.ts | 2 +- .../web-app/app/_ui/project/token-metrics.tsx | 21 +++++++++++++++---- 5 files changed, 52 insertions(+), 6 deletions(-) diff --git a/packages/web-app/app/_lib/hooks.tsx b/packages/web-app/app/_lib/hooks.tsx index 85ffe087..1d78cbe3 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'; @@ -246,3 +248,20 @@ export const useEffectSafe = (callback: () => void, deps: any[]) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, deps); }; + +export const useCtzndRisingTideCap = () => { + const totalInvested = useTotalInvestedUsdcCtznd(); + const aboveCap = Number(totalInvested) > 1_000_000; + 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/token-metrics.tsx b/packages/web-app/app/_ui/project/token-metrics.tsx index 2ed74733..9966dd50 100644 --- a/packages/web-app/app/_ui/project/token-metrics.tsx +++ b/packages/web-app/app/_ui/project/token-metrics.tsx @@ -7,6 +7,7 @@ import { formatEther } from 'viem'; import clsx from 'clsx'; import { number } from '../utils/intl-formaters/number'; import { useTotalInvestedUsdcCtznd } from '@/app/_lib/queries'; +import { useCtzndRisingTideCap } from '@/app/_lib/hooks'; const ProgressBar = ({ title, @@ -17,8 +18,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; @@ -94,11 +95,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,7 +152,9 @@ export const TokenMetrics = () => { Current max. allocation/participant: - {'N/A'} + + {isLoadingCap ? : cap} +
From 122deeb9d382174cc825465df28ce5fcac0b6495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Torres?= Date: Fri, 3 May 2024 13:41:34 +0100 Subject: [PATCH 2/5] Add more scenarios --- packages/web-app/app/_lib/hooks.tsx | 30 +++++++++++- .../app/_ui/project/project-content.tsx | 40 ++++++++++------ .../web-app/app/_ui/project/token-metrics.tsx | 46 ++++++++++++++++++- 3 files changed, 98 insertions(+), 18 deletions(-) diff --git a/packages/web-app/app/_lib/hooks.tsx b/packages/web-app/app/_lib/hooks.tsx index 1d78cbe3..a9981337 100644 --- a/packages/web-app/app/_lib/hooks.tsx +++ b/packages/web-app/app/_lib/hooks.tsx @@ -16,6 +16,8 @@ import { useReadCtzndSalePaymentToken, useReadCtzndSalePaymentTokenToToken, useReadCtzndErc20Allowance, + useReadCtzndSaleMaxTarget, + useReadCtzndSaleMinTarget, } from '@/wagmi.generated'; import { formatEther, parseEther } from 'viem'; import { sepolia } from 'viem/chains'; @@ -249,9 +251,33 @@ export const useEffectSafe = (callback: () => void, deps: any[]) => { }, deps); }; -export const useCtzndRisingTideCap = () => { +export const useCtzndSaleCapStatus = () => { const totalInvested = useTotalInvestedUsdcCtznd(); - const aboveCap = Number(totalInvested) > 1_000_000; + 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'; 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 9966dd50..255b0a44 100644 --- a/packages/web-app/app/_ui/project/token-metrics.tsx +++ b/packages/web-app/app/_ui/project/token-metrics.tsx @@ -7,7 +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 } from '@/app/_lib/hooks'; +import { useCtzndRisingTideCap, useCtzndSaleCapStatus } from '@/app/_lib/hooks'; +import Link from 'next/link'; const ProgressBar = ({ title, @@ -86,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; @@ -157,6 +198,7 @@ export const TokenMetrics = () => {
+ {hasGrant ? : null} ); }; From eb86d91d452f0a345e5cbcf1df9622a7ca77d8e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Torres?= Date: Fri, 3 May 2024 14:04:16 +0100 Subject: [PATCH 3/5] Add steps --- .../web-app/app/_ui/project/apply-button.tsx | 2 +- .../project/citizend-project-description.tsx | 363 +++++++++++++++++- 2 files changed, 346 insertions(+), 19 deletions(-) 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..8123a75f 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,357 @@ +const StepOne = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + +); + +const StepTwo = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + +); + +const StepThree = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + +); + 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. +

+

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.

); From 56f4fa0759e8794ab853d913cff7d72ab66a497f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Torres?= Date: Fri, 3 May 2024 14:06:29 +0100 Subject: [PATCH 4/5] fix svg props --- .../project/citizend-project-description.tsx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) 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 8123a75f..aa8ef38d 100644 --- a/packages/web-app/app/_ui/project/citizend-project-description.tsx +++ b/packages/web-app/app/_ui/project/citizend-project-description.tsx @@ -30,9 +30,9 @@ const StepOne = () => ( width="78.0131" height="78.0131" filterUnits="userSpaceOnUse" - color-interpolation-filters="sRGB" + colorInterpolationFilters="sRGB" > - + ( width="56.8059" height="57.23" filterUnits="userSpaceOnUse" - color-interpolation-filters="sRGB" + colorInterpolationFilters="sRGB" > - + ( width="81.4486" height="81.4486" filterUnits="userSpaceOnUse" - color-interpolation-filters="sRGB" + colorInterpolationFilters="sRGB" > - + ( width="60.2414" height="60.6656" filterUnits="userSpaceOnUse" - color-interpolation-filters="sRGB" + colorInterpolationFilters="sRGB" > - + ( width="78.0131" height="78.0131" filterUnits="userSpaceOnUse" - color-interpolation-filters="sRGB" + colorInterpolationFilters="sRGB" > - + ( width="56.8059" height="57.23" filterUnits="userSpaceOnUse" - color-interpolation-filters="sRGB" + colorInterpolationFilters="sRGB" > - + Date: Fri, 3 May 2024 14:34:21 +0100 Subject: [PATCH 5/5] Add description copy --- .../project/citizend-project-description.tsx | 119 ++++++++++++++++-- packages/web-app/public/fractal.png | Bin 0 -> 4683 bytes packages/web-app/public/outlier.png | Bin 0 -> 6713 bytes 3 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 packages/web-app/public/fractal.png create mode 100644 packages/web-app/public/outlier.png 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 aa8ef38d..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,3 +1,7 @@ +import Image from 'next/image'; +import fractal from '../../../public/fractal.png'; +import outlier from '../../../public/outlier.png'; + const StepOne = () => ( ( ); +export const Arbitrum = () => ( + + + + + + + + + + + + + + + + +); + export const CitizendProjectDescription = () => { return (
@@ -272,7 +322,7 @@ export const CitizendProjectDescription = () => { thrives on the merits of consensus and shared values. Created by the community, for the community.highest potential.

-

How does it work

+

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 @@ -285,10 +335,10 @@ export const CitizendProjectDescription = () => {

-
+
-
+

Explore

Explore projects, sign up, and start your KYC process to prepare @@ -297,10 +347,10 @@ export const CitizendProjectDescription = () => {

-
+
-
+

Vote

Cast your single vote for your favorite project to help determine @@ -309,10 +359,10 @@ export const CitizendProjectDescription = () => {

-
+
-
+

Contribute

Contribute to the winning project using the Two-pool contribution @@ -327,7 +377,58 @@ export const CitizendProjectDescription = () => { users. List of our partners and backers include Fractal ID, Outlier Ventures, Arbitrum and more.

-

The rising tide mechanism

+
+
+
+ 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, @@ -344,7 +445,7 @@ export const CitizendProjectDescription = () => { platform that values community engagement and contribution equally, paving the way for a more inclusive and balanced web3 funding ecosystem.

-

Privacy and Compliance with idOS

+

Privacy and Compliance with idOS

Citizend places a strong emphasis on ensuring the confidentiality and security of its users’ information. Leveraging idOS, a state-of-the-art diff --git a/packages/web-app/public/fractal.png b/packages/web-app/public/fractal.png new file mode 100644 index 0000000000000000000000000000000000000000..06abea71c2c070a201e6076ff25115aa56f5cacc GIT binary patch literal 4683 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 P00000NkvXXu0mjf{=LyD literal 0 HcmV?d00001