From 26636adb229f6c00cad7aaf893393c96e92de391 Mon Sep 17 00:00:00 2001 From: Aditya Anand M C Date: Tue, 4 Feb 2025 12:58:29 +0530 Subject: [PATCH 1/3] add base --- config/contracts/addresses.ts | 2 ++ config/features.ts | 25 ++++++++++++++++++++++-- public/base-bg-img.svg | 4 ++++ public/base-logo_dark.svg | 3 +++ src/components/common/images/BgImage.tsx | 1 + src/components/common/images/Logo.tsx | 3 +++ src/lib/getPublicClientForChain.ts | 7 +++++++ 7 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 public/base-bg-img.svg create mode 100644 public/base-logo_dark.svg diff --git a/config/contracts/addresses.ts b/config/contracts/addresses.ts index 9a44a06..97d35d8 100644 --- a/config/contracts/addresses.ts +++ b/config/contracts/addresses.ts @@ -1,4 +1,5 @@ import { + base, mainnet, optimism, sepolia, @@ -10,6 +11,7 @@ export const hedgeyContractAddresses = { [optimism.id]: '0x8A2725a6f04816A5274dDD9FEaDd3bd0C253C1A6', [mainnet.id]: '0x8A2725a6f04816A5274dDD9FEaDd3bd0C253C1A6', [sepolia.id]: '0x8A2725a6f04816A5274dDD9FEaDd3bd0C253C1A6', + [base.id]: '0x8A2725a6f04816A5274dDD9FEaDd3bd0C253C1A6', // TODO: Add correct contract addresses for zkSync once they have been deployed [zksync.id]: '0x83FD45623D1627258D5e336e8BaeE3796F47a1C5', [zksyncSepoliaTestnet.id]: '0xUnknown', diff --git a/config/features.ts b/config/features.ts index b734bfd..ebebfdb 100644 --- a/config/features.ts +++ b/config/features.ts @@ -1,5 +1,6 @@ import colors from 'tailwindcss/colors'; import { + base, mainnet, optimism, optimismSepolia, @@ -8,7 +9,7 @@ import { zksyncSepoliaTestnet, } from 'wagmi/chains'; -type WHITELABEL_ENV = 'OPTIMISM' | 'ZK_SYNC'; +type WHITELABEL_ENV = 'OPTIMISM' | 'ZK_SYNC' | 'BASE'; const _WHITELABEL_ENV = process.env.NEXT_PUBLIC_WHITELABEL_ENV; @@ -16,7 +17,7 @@ if (!_WHITELABEL_ENV) { throw new Error('NEXT_PUBLIC_WHITELABEL_ENV is not set'); } -if (!(_WHITELABEL_ENV === 'OPTIMISM' || _WHITELABEL_ENV === 'ZK_SYNC')) { +if (!['OPTIMISM', 'ZK_SYNC', 'BASE'].includes(_WHITELABEL_ENV)) { throw new Error('NEXT_PUBLIC_WHITELABEL_ENV is not set to a valid value'); } @@ -54,6 +55,15 @@ const featureMatrix: Record = { DELEGATES_URL: 'https://vote.zknation.io/dao/delegates', CONFIRMATION_CHECKMARK_BG_COLOR: 'black', }, + BASE: { + APP_NAME: 'Base Claim Tool', + BG_IMAGE: { + src: '/base-bg-img.svg', + }, + DELEGATION_REQUIRED: false, + DELEGATION_ENABLED: false, + CONFIRMATION_CHECKMARK_BG_COLOR: '#0052FF', + }, }; export const FEATURES = featureMatrix[_WHITELABEL_ENV]; @@ -76,6 +86,11 @@ export const getChainConfig = () => { appName: 'ZKsync Claim Tool', chains: [mainnet, zksync, zksyncSepoliaTestnet], }; + case 'BASE': + return { + appName: 'Base Claim Tool', + chains: [base], + }; } }; @@ -99,5 +114,11 @@ export const getWhitelabelThemeColors = (): WhitelabelThemeColors => { primaryAction: colors.blue[500], primaryActionButtonBg: colors.blue[900], }; + case 'BASE': + return { + bgClaimcardHeader: colors.blue[100], + primaryAction: colors.blue[400], + primaryActionButtonBg: colors.blue[700], + }; } }; diff --git a/public/base-bg-img.svg b/public/base-bg-img.svg new file mode 100644 index 0000000..22dc4e3 --- /dev/null +++ b/public/base-bg-img.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/base-logo_dark.svg b/public/base-logo_dark.svg new file mode 100644 index 0000000..8f07365 --- /dev/null +++ b/public/base-logo_dark.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/components/common/images/BgImage.tsx b/src/components/common/images/BgImage.tsx index 0275d4e..d6426a7 100644 --- a/src/components/common/images/BgImage.tsx +++ b/src/components/common/images/BgImage.tsx @@ -4,6 +4,7 @@ import { WHITELABEL_ENV } from '../../../../config/features'; const getClassName = () => { switch (WHITELABEL_ENV) { case 'ZK_SYNC': + case 'BASE': return 'opacity-10'; default: return ''; diff --git a/src/components/common/images/Logo.tsx b/src/components/common/images/Logo.tsx index 78559c5..59aca5e 100644 --- a/src/components/common/images/Logo.tsx +++ b/src/components/common/images/Logo.tsx @@ -1,5 +1,6 @@ import Image from 'next/image'; import { WHITELABEL_ENV } from '../../../../config/features'; +import BaseLogo from '../../../../public/base-logo_dark.svg'; import OpLogo from '../../../../public/op-logo.svg'; import ZkSyncLogo from '../../../../public/zksync_logo_dark.svg'; const Logo = () => { @@ -8,6 +9,8 @@ const Logo = () => { return OP Logo; case 'ZK_SYNC': return ZKSync Logo; + case 'BASE': + return Base Logo; default: throw new Error('Invalid WHITE_LABEL_ENV'); } diff --git a/src/lib/getPublicClientForChain.ts b/src/lib/getPublicClientForChain.ts index e5b3d43..4d8d5ec 100644 --- a/src/lib/getPublicClientForChain.ts +++ b/src/lib/getPublicClientForChain.ts @@ -1,5 +1,6 @@ import { http, createPublicClient, isAddress } from 'viem'; import { + base, mainnet, optimism, optimismSepolia, @@ -19,6 +20,8 @@ export const getChainForChainId = (chainId: number) => { return optimismSepolia; case zksync.id: return zksync; + case base.id: + return base; default: throw new Error(`Unsupported chain: ${chainId}`); } @@ -36,6 +39,8 @@ const getRpcUrlForChain = (chainId: number) => { return 'https://sepolia.optimism.io'; case zksync.id: return 'https://mainnet.era.zksync.io'; + case base.id: + return 'https://mainnet.base.org'; default: throw new Error(`Unsupported chain: ${chainId}`); } @@ -53,6 +58,8 @@ export const getChainIdByNetworkName = (networkName: string | null) => { return optimismSepolia.id; case 'zksync-era': return zksync.id; + case 'base': + return base.id; default: throw new Error(`Unsupported network name: ${networkName}`); } From 8616562e264b3ca8f6288163283abdabdf614ba8 Mon Sep 17 00:00:00 2001 From: Aditya Anand M C Date: Thu, 6 Feb 2025 11:56:57 +0530 Subject: [PATCH 2/3] update to match design --- .env.example | 1 + README.md | 5 + config/features.ts | 10 +- public/base-bg-img.svg | 4 - public/base-logo.svg | 4 + public/base-logo_dark.svg | 3 - src/app/api/grants/route.ts | 1 - src/app/claim-history/page.tsx | 3 +- src/app/grants/[grantId]/page.tsx | 8 +- src/app/grants/page.tsx | 41 ++-- src/app/layout.tsx | 10 +- src/components/auth/ConnectButton.tsx | 17 +- src/components/common/ClaimCard.tsx | 11 +- src/components/common/GrantCard.tsx | 178 +++++++++--------- src/components/common/ProjectCard.tsx | 6 +- src/components/common/images/Logo.tsx | 2 +- src/components/common/images/ProjectImage.tsx | 4 +- .../common/skeletons/GrantCardSkeleton.tsx | 2 +- src/hooks/useGetGrants.ts | 8 + src/lib/getPublicClientForChain.ts | 2 +- 20 files changed, 173 insertions(+), 147 deletions(-) delete mode 100644 public/base-bg-img.svg create mode 100644 public/base-logo.svg delete mode 100644 public/base-logo_dark.svg diff --git a/.env.example b/.env.example index 3d12a59..ebe1338 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,7 @@ # FRONTEND NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=bab3792638df5214933091cdfc569452 NEXT_PUBLIC_WHITELABEL_ENV="OPTIMISM" +NEXT_PUBLIC_ROUND_NAME="OP Citizen Grants Council" # RAILWAY RAILWAY_ENVIRONMENT_NAME="LOCAL_DEV" diff --git a/README.md b/README.md index 9bae1ae..14e6382 100644 --- a/README.md +++ b/README.md @@ -60,3 +60,8 @@ The OP Claim Tool is a web application for Optimism grant recipients to claim an - `pnpm format`: Run Biome formatter For more details, refer to the project documentation. + +## Testing +- For testing use sepolia +- [Mint PLBR token](https://eth-sepolia.blockscout.com/token/0xdF0A43D15B036c065f6895734B878fD31269Bfa3?tab=read_write_contract) +- Create a hedgey token grant diff --git a/config/features.ts b/config/features.ts index ebebfdb..3c8ce90 100644 --- a/config/features.ts +++ b/config/features.ts @@ -23,9 +23,12 @@ if (!['OPTIMISM', 'ZK_SYNC', 'BASE'].includes(_WHITELABEL_ENV)) { export const WHITELABEL_ENV = _WHITELABEL_ENV; +export const ROUND_NAME = + process.env.NEXT_PUBLIC_ROUND_NAME || `${WHITELABEL_ENV} Round`; + interface Features { APP_NAME: string; - BG_IMAGE: { + BG_IMAGE?: { src: string; }; DELEGATION_REQUIRED: boolean; @@ -57,9 +60,6 @@ const featureMatrix: Record = { }, BASE: { APP_NAME: 'Base Claim Tool', - BG_IMAGE: { - src: '/base-bg-img.svg', - }, DELEGATION_REQUIRED: false, DELEGATION_ENABLED: false, CONFIRMATION_CHECKMARK_BG_COLOR: '#0052FF', @@ -89,7 +89,7 @@ export const getChainConfig = () => { case 'BASE': return { appName: 'Base Claim Tool', - chains: [base], + chains: [base, sepolia], }; } }; diff --git a/public/base-bg-img.svg b/public/base-bg-img.svg deleted file mode 100644 index 22dc4e3..0000000 --- a/public/base-bg-img.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/public/base-logo.svg b/public/base-logo.svg new file mode 100644 index 0000000..67c4322 --- /dev/null +++ b/public/base-logo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/public/base-logo_dark.svg b/public/base-logo_dark.svg deleted file mode 100644 index 8f07365..0000000 --- a/public/base-logo_dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/src/app/api/grants/route.ts b/src/app/api/grants/route.ts index 320a1b5..1f6d140 100644 --- a/src/app/api/grants/route.ts +++ b/src/app/api/grants/route.ts @@ -68,7 +68,6 @@ export async function GET(req: NextRequest) { ); const sheetNames = await sheetNamesResponse.json(); const title: string = sheetNames?.sheets[0]?.properties?.title; - if (!title) { return Response.json({ success: false, diff --git a/src/app/claim-history/page.tsx b/src/app/claim-history/page.tsx index 496d5e8..bd5465f 100644 --- a/src/app/claim-history/page.tsx +++ b/src/app/claim-history/page.tsx @@ -77,7 +77,8 @@ const ClaimHistory = () => {

Remaining:

- {Math.round(grant.grantAmount - grant.claimed)}{' '} + {Math.round(grant.grantAmount - grant.claimed) || + 0}{' '} {grant.campaign.token?.ticker}

diff --git a/src/app/grants/[grantId]/page.tsx b/src/app/grants/[grantId]/page.tsx index 69ab3cd..3a9aa16 100644 --- a/src/app/grants/[grantId]/page.tsx +++ b/src/app/grants/[grantId]/page.tsx @@ -33,11 +33,13 @@ const GrantPage = () => { Back to all grants -

You're claiming

+

You're claiming for

- {grant?.title} + + {grant?.title} + with - + {grant?.grantAmount} {grant?.campaign.token?.ticker} diff --git a/src/app/grants/page.tsx b/src/app/grants/page.tsx index 75ad1a2..e856475 100644 --- a/src/app/grants/page.tsx +++ b/src/app/grants/page.tsx @@ -4,7 +4,6 @@ import WalletConnectButton from '@/components/auth/ConnectButton'; import GrantsList from '@/components/common/GrantList'; import { GrantCardSkeleton } from '@/components/common/skeletons/GrantCardSkeleton'; import { Button } from '@/components/ui/button'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Select, @@ -16,7 +15,8 @@ import { import { FilterOption, useGrants } from '@/context/GrantsContext'; import { Search } from 'lucide-react'; import { useMemo, useState } from 'react'; -import { useAccount, useConnectorClient } from 'wagmi'; +import { useAccount } from 'wagmi'; +import { ROUND_NAME } from '../../../config/features'; const Grants = () => { const { displayedGrants, loadMore, grants, isLoading, isFetched } = @@ -24,7 +24,6 @@ const Grants = () => { const [searchTerm, setSearchTerm] = useState(''); const [filter, setFilter] = useState(FilterOption.Highest); - const { isFetched: isFetchedConnector } = useConnectorClient(); const { isConnected } = useAccount(); const filteredAndSortedGrants = useMemo(() => { @@ -60,18 +59,6 @@ const Grants = () => { return filtered; }, [displayedGrants, searchTerm, filter]); - if (isFetchedConnector && !isConnected) { - // Display a card to connect wallet, with connect button - return ( - - - Connect your wallet to view grants - - - - ); - } - if (!isFetched && !isLoading) { return null; } @@ -81,17 +68,24 @@ const Grants = () => {

Grants

- Explore all grants from the OP Citizen Grants Council and who they've - delegated to. For grantees, this claiming tool offers a self-serve - interface to claim and delegate your grant. + Explore all grants from the {ROUND_NAME} and who they've delegated to. + For grantees, this claiming tool offers a self-serve interface to + claim and delegate your grant.

+ + {!isConnected && ( + + )}
setSearchTerm(e.target.value)} /> @@ -101,7 +95,7 @@ const Grants = () => { value={filter} onValueChange={(value) => setFilter(value as FilterOption)} > - + @@ -112,15 +106,16 @@ const Grants = () => {
-
-

+

+

{isLoading ? 'Loading' : grants.length} Projects

-

+

Claimed /{' '} Grant amount

+ {isLoading ? (
diff --git a/src/app/layout.tsx b/src/app/layout.tsx index bb85cc8..6eaf0ca 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -30,13 +30,15 @@ export default function RootLayout({ }: { children: React.ReactNode }) { return ( - + {children} -
- -
+ {FEATURES.BG_IMAGE && ( +
+ +
+ )} diff --git a/src/components/auth/ConnectButton.tsx b/src/components/auth/ConnectButton.tsx index 743aafc..7eaa3ca 100644 --- a/src/components/auth/ConnectButton.tsx +++ b/src/components/auth/ConnectButton.tsx @@ -1,6 +1,7 @@ 'use client'; import { useDisconnect } from '@/hooks/useAuth'; +import { cn } from '@/lib/utils'; import { ConnectButton } from '@rainbow-me/rainbowkit'; import { RiFileHistoryLine, RiLogoutBoxRLine } from '@remixicon/react'; import { useRouter } from 'next/navigation'; @@ -11,8 +12,13 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from '../ui/dropdown-menu'; - -const WalletConnectButton = () => { +const WalletConnectButton = ({ + text, + classNames, +}: { + text?: string; + classNames?: string; +}) => { const { disconnect } = useDisconnect(); return ( @@ -49,11 +55,14 @@ const WalletConnectButton = () => { return ( ); } diff --git a/src/components/common/ClaimCard.tsx b/src/components/common/ClaimCard.tsx index 7ad227d..a8edac3 100644 --- a/src/components/common/ClaimCard.tsx +++ b/src/components/common/ClaimCard.tsx @@ -188,7 +188,7 @@ export default function ClaimCard({ grant }: { grant: Grant }) { />
{DELEGATES_URL && ( -

+

You can visit{' '} ) : ( -

+

+ Excellent. You are now ready to claim your rewards. +

)}
+
+ + {step === 'form' && ( + + + + {DELEGATION_REQUIRED ? ( + <> +
+ ( + + + Enter the delegate's address + + + + + + + )} + /> +
+ {DELEGATES_URL && ( +

+ You can visit{' '} + + this page + {' '} + to find the delegate who should represent for you, or + delegate the token to yourself. +

+ )} + + ) : ( +

+ Excellent. You are now ready to claim your rewards. +

+ )} +
+ + + + + + )} + {step === 'confirmation' && ( + +

All done!

+ +
+ {txHash && ( + +
- {DELEGATES_URL && ( -

- You can visit{' '} - - this page - {' '} - to find the delegate who should represent for you, or - delegate the token to yourself. -

- )} - - ) : ( -

- Excellent. You are now ready to claim your rewards. -

+ + )} -
- - - - - - )} - {step === 'confirmation' && ( - -

All done!

- -
- {txHash && ( - - - - )} -
-
- )} -
+
+ + )} + + ); } diff --git a/src/components/common/GrantCard.tsx b/src/components/common/GrantCard.tsx index 4234282..915db7a 100644 --- a/src/components/common/GrantCard.tsx +++ b/src/components/common/GrantCard.tsx @@ -61,7 +61,7 @@ const GrantCard = ({ grant }: { grant: Grant }) => {
{grant.campaign.ended && ( - Cancelled + Cancelled )} {grant.proof?.claimed && ( Claimed