├── .dockerignore ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── scripts │ └── preview.py └── workflows │ ├── action-jira-linker.yml │ ├── ci.yml │ ├── unit-tests.yml │ └── vercel-preview-maker.yml ├── .gitignore ├── .hintrc ├── .prettierrc ├── .tool-versions ├── .vscode ├── launch.json └── settings.json ├── Dockerfile ├── Dockerfile.app ├── Dockerfile.base ├── LICENSE ├── README.md ├── apps └── nouns │ └── node_modules │ └── tsconfig ├── components.json ├── cypress.config.ts ├── cypress ├── e2e │ └── agora.cy.ts ├── fixtures │ └── example.json └── support │ ├── commands.ts │ └── e2e.ts ├── env.sample ├── funding.json ├── next.config.js ├── package.json ├── packages ├── database │ └── node_modules │ │ └── tsconfig └── ui │ └── node_modules │ └── tsconfig ├── postcss.config.js ├── prisma └── schema.prisma ├── public ├── .well-known │ └── walletconnect.txt ├── favicon │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── browserconfig.xml │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── mstile-144x144.png │ ├── mstile-150x150.png │ ├── mstile-310x150.png │ ├── mstile-310x310.png │ ├── mstile-70x70.png │ ├── safari-pinned-tab.svg │ └── site.webmanifest ├── fonts │ ├── Inter │ │ ├── Inter-Black.ttf │ │ ├── Inter-Bold.ttf │ │ ├── Inter-Medium.ttf │ │ └── Inter-Regular.ttf │ ├── Rubik-Bold.ttf │ ├── Rubik-BoldItalic.ttf │ ├── Rubik-Medium.ttf │ ├── Rubik-Regular.ttf │ └── TransSansPremium │ │ ├── TransSansPremium_Bold.otf │ │ ├── TransSansPremium_Light.otf │ │ ├── TransSansPremium_Regular.otf │ │ └── TransSansPremium_SemiBold.otf ├── images │ ├── blink.gif │ ├── ens_success.svg │ ├── ens_temp_check.png │ ├── grid-share.png │ ├── grid.svg │ ├── og-generic-bg.png │ └── receipt_bg.svg ├── next.svg ├── rpgf │ ├── infographic_2.png │ ├── infographic_3.png │ ├── infographic_4.png │ ├── infographic_5.png │ ├── infographic_6.png │ ├── infographic_7.png │ ├── infographic_8.png │ ├── infographic_9.png │ └── overlay.png └── vercel.svg ├── setupTests.ts ├── spec ├── api_v1.yaml └── oas_v1.yaml ├── src ├── __mocks__ │ └── fonts.ts ├── app │ ├── Web3Provider.tsx │ ├── admin │ │ ├── actions.ts │ │ ├── loading.tsx │ │ ├── membership │ │ │ └── page.tsx │ │ └── page.tsx │ ├── api │ │ ├── analytics │ │ │ ├── metric │ │ │ │ └── [metric_id] │ │ │ │ │ └── [frequency] │ │ │ │ │ ├── getMetricsTS.ts │ │ │ │ │ └── route.ts │ │ │ ├── top │ │ │ │ └── delegates │ │ │ │ │ ├── getTopDelegateWeighs.ts │ │ │ │ │ └── route.ts │ │ │ ├── track │ │ │ │ └── route.ts │ │ │ └── vote │ │ │ │ ├── getProposalVoteCounts.ts │ │ │ │ └── route.ts │ │ ├── balances │ │ │ └── [frequency] │ │ │ │ ├── getTreasuryBalanceTS.ts │ │ │ │ └── route.ts │ │ ├── common │ │ │ ├── authority-chains │ │ │ │ ├── authorityChains.d.ts │ │ │ │ └── getAuthorityChains.ts │ │ │ ├── badgeholders │ │ │ │ └── getBadgeholders.ts │ │ │ ├── ballots │ │ │ │ ├── autobalance.ts │ │ │ │ ├── ballot.d.ts │ │ │ │ ├── ballotAllocations.test.ts │ │ │ │ ├── ballotAllocations.ts │ │ │ │ ├── ballotDistributionStrategy.ts │ │ │ │ ├── getBallots.ts │ │ │ │ ├── submitBallot.ts │ │ │ │ ├── updateBallot.ts │ │ │ │ ├── updateBallotCategories.ts │ │ │ │ └── updateBallotProject.ts │ │ │ ├── changelogs │ │ │ │ ├── changelog.d.ts │ │ │ │ └── getChangelogs.ts │ │ │ ├── citizens │ │ │ │ ├── getCitizens.ts │ │ │ │ └── isCitizen.ts │ │ │ ├── comments │ │ │ │ ├── createImpactMetricComment.ts │ │ │ │ ├── deleteImpactMetricComment.ts │ │ │ │ ├── getImpactMetricCommentVotes.ts │ │ │ │ ├── getImpactMetricComments.ts │ │ │ │ ├── impactMetricComment.d.ts │ │ │ │ ├── updateImpactMetricComment.ts │ │ │ │ └── updateImpactMetricCommentVote.ts │ │ │ ├── delegateStatement │ │ │ │ ├── createDelegateStatement.ts │ │ │ │ ├── delegateStatement.d.ts │ │ │ │ └── getDelegateStatement.ts │ │ │ ├── delegates │ │ │ │ ├── delegate.d.ts │ │ │ │ ├── getDelegateForSCW.ts │ │ │ │ └── getDelegates.ts │ │ │ ├── delegations │ │ │ │ ├── delegation.d.ts │ │ │ │ └── getDelegations.ts │ │ │ ├── draftProposals │ │ │ │ └── getDraftProposals.ts │ │ │ ├── governanceCalendar │ │ │ │ └── getGovernanceCalendar.ts │ │ │ ├── impactMetrics │ │ │ │ ├── getImpactMetrics.ts │ │ │ │ └── viewImactMetric.ts │ │ │ ├── metrics │ │ │ │ ├── getMetrics.ts │ │ │ │ ├── metrics.d.ts │ │ │ │ └── route.ts │ │ │ ├── notifications │ │ │ │ └── updateNotificationPreferencesForAddress.ts │ │ │ ├── projects │ │ │ │ ├── getProjects.ts │ │ │ │ └── project.d.ts │ │ │ ├── proposals │ │ │ │ ├── getNeedsMyVoteProposals.ts │ │ │ │ ├── getProposals.ts │ │ │ │ └── proposal.d.ts │ │ │ ├── quorum │ │ │ │ └── getQuorum.ts │ │ │ ├── rounds │ │ │ │ └── getRetroFundingRounds.ts │ │ │ ├── utils │ │ │ │ ├── bigIntRatio.test.ts │ │ │ │ ├── bigIntRatio.ts │ │ │ │ ├── ensName.ts │ │ │ │ ├── frequencyHandling.ts │ │ │ │ └── validators.ts │ │ │ ├── votableSupply │ │ │ │ ├── getVotableSupply.ts │ │ │ │ └── route.ts │ │ │ ├── votes │ │ │ │ ├── getVotes.ts │ │ │ │ ├── route.ts │ │ │ │ └── vote.d.ts │ │ │ └── voting-power │ │ │ │ ├── getVotingPower.ts │ │ │ │ └── votingPower.d.ts │ │ ├── delegations │ │ │ └── getDelegations.ts │ │ ├── images │ │ │ └── og │ │ │ │ ├── assets │ │ │ │ ├── Inter-Black.ttf │ │ │ │ ├── Inter-Bold.ttf │ │ │ │ ├── Inter-Medium.ttf │ │ │ │ ├── Inter-Regular.ttf │ │ │ │ ├── Inter-SemiBold.ttf │ │ │ │ ├── grid-share.png │ │ │ │ ├── og-delegate-bg.png │ │ │ │ ├── og-delegates-bg.png │ │ │ │ ├── og-generic-bg.png │ │ │ │ ├── og-proposals-bg.png │ │ │ │ └── shared.tsx │ │ │ │ ├── delegate │ │ │ │ └── route.tsx │ │ │ │ ├── delegates │ │ │ │ └── route.tsx │ │ │ │ ├── generic │ │ │ │ └── route.tsx │ │ │ │ ├── proposals │ │ │ │ └── route.tsx │ │ │ │ └── share-my-vote │ │ │ │ └── route.tsx │ │ ├── paymaster │ │ │ └── fetchPaymasterData.ts │ │ ├── proposals │ │ │ ├── [proposalId] │ │ │ │ ├── chart │ │ │ │ │ └── route.ts │ │ │ │ ├── copeland-result │ │ │ │ │ └── route.ts │ │ │ │ └── votes-csv │ │ │ │ │ └── route.ts │ │ │ └── getVotesChart.ts │ │ ├── simulate-bundle │ │ │ └── route.ts │ │ ├── simulate │ │ │ └── route.ts │ │ ├── staking │ │ │ ├── getDeposit.ts │ │ │ └── getDeposits.ts │ │ ├── v1 │ │ │ ├── apiUtils.ts │ │ │ ├── auth │ │ │ │ ├── nonce │ │ │ │ │ └── route.ts │ │ │ │ └── verify │ │ │ │ │ └── route.ts │ │ │ ├── contracts │ │ │ │ ├── alligator │ │ │ │ │ └── route.ts │ │ │ │ ├── governor │ │ │ │ │ └── route.ts │ │ │ │ └── token │ │ │ │ │ └── route.ts │ │ │ ├── delegates │ │ │ │ ├── [addressOrENSName] │ │ │ │ │ ├── delegatees │ │ │ │ │ │ └── route.ts │ │ │ │ │ ├── delegators │ │ │ │ │ │ └── route.ts │ │ │ │ │ ├── route.ts │ │ │ │ │ └── votes │ │ │ │ │ │ └── route.ts │ │ │ │ └── route.ts │ │ │ ├── projects │ │ │ │ └── route.ts │ │ │ ├── proposals │ │ │ │ ├── [proposalId] │ │ │ │ │ ├── route.ts │ │ │ │ │ └── votes │ │ │ │ │ │ └── route.ts │ │ │ │ └── route.ts │ │ │ ├── relay │ │ │ │ ├── delegate │ │ │ │ │ ├── delegate.ts │ │ │ │ │ └── route.ts │ │ │ │ ├── getRelayStatus.ts │ │ │ │ ├── route.ts │ │ │ │ └── vote │ │ │ │ │ ├── castVote.ts │ │ │ │ │ └── route.ts │ │ │ ├── retrofunding │ │ │ │ └── rounds │ │ │ │ │ ├── [roundId] │ │ │ │ │ ├── ballots │ │ │ │ │ │ └── [ballotCasterAddressOrEns] │ │ │ │ │ │ │ ├── budget │ │ │ │ │ │ │ └── [budget] │ │ │ │ │ │ │ │ └── route.ts │ │ │ │ │ │ │ ├── categories │ │ │ │ │ │ │ └── route.ts │ │ │ │ │ │ │ ├── distribution_method │ │ │ │ │ │ │ └── [distributionMethod] │ │ │ │ │ │ │ │ └── route.ts │ │ │ │ │ │ │ ├── impactMetrics │ │ │ │ │ │ │ ├── [impactMetricId] │ │ │ │ │ │ │ │ └── route.ts │ │ │ │ │ │ │ └── route.ts │ │ │ │ │ │ │ ├── osMultiplier │ │ │ │ │ │ │ └── [multiplier] │ │ │ │ │ │ │ │ └── route.ts │ │ │ │ │ │ │ ├── osOnly │ │ │ │ │ │ │ └── [toggle] │ │ │ │ │ │ │ │ └── route.ts │ │ │ │ │ │ │ ├── projects │ │ │ │ │ │ │ ├── [projectId] │ │ │ │ │ │ │ │ ├── allocation │ │ │ │ │ │ │ │ │ └── [allocation] │ │ │ │ │ │ │ │ │ │ └── route.ts │ │ │ │ │ │ │ │ ├── impact │ │ │ │ │ │ │ │ │ └── [impact] │ │ │ │ │ │ │ │ │ │ └── route.ts │ │ │ │ │ │ │ │ └── position │ │ │ │ │ │ │ │ │ └── [position] │ │ │ │ │ │ │ │ │ └── route.ts │ │ │ │ │ │ │ └── route.ts │ │ │ │ │ │ │ ├── route.ts │ │ │ │ │ │ │ └── submit │ │ │ │ │ │ │ └── route.ts │ │ │ │ │ ├── impactMetrics │ │ │ │ │ │ ├── [impactMetricId] │ │ │ │ │ │ │ ├── [addressOrENSName] │ │ │ │ │ │ │ │ └── route.ts │ │ │ │ │ │ │ ├── comments │ │ │ │ │ │ │ │ ├── [commentId] │ │ │ │ │ │ │ │ │ ├── route.ts │ │ │ │ │ │ │ │ │ └── votes │ │ │ │ │ │ │ │ │ │ └── route.ts │ │ │ │ │ │ │ │ └── route.ts │ │ │ │ │ │ │ └── route.ts │ │ │ │ │ │ └── route.ts │ │ │ │ │ ├── projects │ │ │ │ │ │ ├── [projectId] │ │ │ │ │ │ │ └── route.ts │ │ │ │ │ │ └── route.ts │ │ │ │ │ └── route.ts │ │ │ │ │ └── route.ts │ │ │ ├── spec │ │ │ │ └── route.ts │ │ │ └── votable_supply │ │ │ │ └── route.ts │ │ └── votes │ │ │ └── getVotes.ts │ ├── changelog │ │ └── page.tsx │ ├── custom.css │ │ └── route.ts │ ├── delegates │ │ ├── [addressOrENSName] │ │ │ ├── ProfileTabs.tsx │ │ │ ├── components │ │ │ │ └── SCWRedirect.tsx │ │ │ ├── error.tsx │ │ │ ├── loading.tsx │ │ │ ├── not-found.tsx │ │ │ ├── page.tsx │ │ │ └── params.ts │ │ ├── actions.ts │ │ ├── create │ │ │ └── page.tsx │ │ ├── edit │ │ │ └── page.tsx │ │ ├── loading.tsx │ │ ├── page.jsx │ │ └── search-params.ts │ ├── icon.png │ ├── info │ │ ├── __tests__ │ │ │ └── GovernorSettingsParams.test.tsx │ │ ├── components │ │ │ ├── ChartGovernanceActiveDelegates.tsx │ │ │ ├── ChartGovernanceAvgVotes.tsx │ │ │ ├── ChartGovernanceRequiredDelegates.tsx │ │ │ ├── ChartGovernanceTopDelegates.tsx │ │ │ ├── ChartGovernanceVotableSupply.tsx │ │ │ ├── ChartTabs.tsx │ │ │ ├── ChartTreasury.tsx │ │ │ ├── ContractList.tsx │ │ │ ├── GovernanceCharts.tsx │ │ │ ├── GovernorSettings.tsx │ │ │ ├── GovernorSettingsParams.tsx │ │ │ ├── GovernorSettingsProposalTypes.tsx │ │ │ ├── InfoAbout.tsx │ │ │ └── InfoHero.tsx │ │ └── page.tsx │ ├── layout.tsx │ ├── lib │ │ ├── ENSUtils.ts │ │ ├── __tests__ │ │ │ └── ENSUtils.test.ts │ │ ├── agoraAPI.js │ │ ├── auth │ │ │ ├── constants.ts │ │ │ ├── edgeAuth.ts │ │ │ ├── serverAuth.ts │ │ │ └── types.ts │ │ ├── dao-node │ │ │ └── client.ts │ │ ├── dynamodb.js │ │ ├── hooks │ │ │ ├── useForm.ts │ │ │ ├── useIsAdvancedUser.ts │ │ │ └── useIsOpManager.ts │ │ ├── logging.ts │ │ ├── middleware │ │ │ └── authenticateAgoraApiUser.js │ │ ├── pagination.ts │ │ ├── prisma.ts │ │ └── utils │ │ │ ├── color.ts │ │ │ ├── og.ts │ │ │ └── text.ts │ ├── loading.tsx │ ├── not-found.tsx │ ├── page.tsx │ ├── proposals │ │ ├── [proposal_id] │ │ │ ├── loading.tsx │ │ │ └── page.tsx │ │ ├── actions.tsx │ │ ├── components │ │ │ ├── AgoraGovCancel.tsx │ │ │ ├── AgoraGovExecute.tsx │ │ │ ├── AgoraGovQueue.tsx │ │ │ ├── AgoraOptimismGovCancel.tsx │ │ │ ├── AgoraOptimismGovExecute.tsx │ │ │ ├── AgoraOptimismGovQueue.tsx │ │ │ ├── BravoGovCancel.tsx │ │ │ ├── BravoGovExecute.tsx │ │ │ ├── BravoGovQueue.tsx │ │ │ ├── OZGovExecute.tsx │ │ │ ├── OZGovQueue.tsx │ │ │ ├── ProposalStateAdmin.tsx │ │ │ ├── UpdateVotableSupplyOracle.tsx │ │ │ └── __tests__ │ │ │ │ └── UpdateVotableSupplyOracle.test.tsx │ │ ├── create │ │ │ └── page.jsx │ │ ├── draft │ │ │ ├── [id] │ │ │ │ ├── components │ │ │ │ │ └── OwnerOnly.tsx │ │ │ │ ├── loading.tsx │ │ │ │ └── page.tsx │ │ │ ├── actions │ │ │ │ ├── createDraftProposal.ts │ │ │ │ ├── createGithubChecklistItem.ts │ │ │ │ ├── createTempCheck.ts │ │ │ │ ├── deleteDraftProposal.ts │ │ │ │ ├── requestSponsorship.ts │ │ │ │ ├── revalidatePath.ts │ │ │ │ └── sponsorDraftProposal.ts │ │ │ ├── components │ │ │ │ ├── ApprovalProposalForm.tsx │ │ │ │ ├── ArchivedDraftProposal.tsx │ │ │ │ ├── AvatarAdress.tsx │ │ │ │ ├── BackButton.tsx │ │ │ │ ├── BasicProposalForm.tsx │ │ │ │ ├── CreatorAuthCheck.tsx │ │ │ │ ├── CustomTransactionForm.tsx │ │ │ │ ├── DeleteDraftButton.tsx │ │ │ │ ├── DraftPreview.tsx │ │ │ │ ├── DraftProposalForm.tsx │ │ │ │ ├── OptimisticProposalForm.tsx │ │ │ │ ├── RequestSponsorshipForm.tsx │ │ │ │ ├── SimulateStatusPill.tsx │ │ │ │ ├── SocialProposalForm.tsx │ │ │ │ ├── TransferTransactionForm.tsx │ │ │ │ ├── dialogs │ │ │ │ │ ├── AddGithubPRDialog.tsx │ │ │ │ │ ├── CreateDraftProposalDialog.tsx │ │ │ │ │ ├── SponsorOnchainProposalDialog.tsx │ │ │ │ │ ├── SponsorSnapshotProposalDialog.tsx │ │ │ │ │ └── UpdateDraftProposalDialog.tsx │ │ │ │ ├── form │ │ │ │ │ ├── AddressInput.tsx │ │ │ │ │ ├── DateInput.tsx │ │ │ │ │ ├── FormCard.tsx │ │ │ │ │ ├── FormItem.tsx │ │ │ │ │ ├── MarkdownTextareaInput.tsx │ │ │ │ │ ├── NumberInput.tsx │ │ │ │ │ ├── RadioGroupInput.tsx │ │ │ │ │ ├── SelectInput.tsx │ │ │ │ │ ├── SwitchInput.tsx │ │ │ │ │ └── TextInput.tsx │ │ │ │ └── stages │ │ │ │ │ ├── DraftForm │ │ │ │ │ └── DraftFormClient.tsx │ │ │ │ │ ├── GithubPRForm.tsx │ │ │ │ │ ├── SubmitForm.tsx │ │ │ │ │ └── TempCheckForm.tsx │ │ │ ├── schemas │ │ │ │ ├── DraftProposalSchema.ts │ │ │ │ ├── requestSponsorshipSchema.ts │ │ │ │ ├── sponsorProposalSchema.ts │ │ │ │ └── tempCheckSchema.ts │ │ │ ├── types.ts │ │ │ └── utils │ │ │ │ ├── __tests__ │ │ │ │ └── proposalTypes.ts │ │ │ │ ├── createSnapshot.ts │ │ │ │ ├── formatTransactions.ts │ │ │ │ ├── getInputData.ts │ │ │ │ ├── github.ts │ │ │ │ ├── proposalTypes.ts │ │ │ │ └── stages.ts │ │ ├── loading.tsx │ │ ├── page.tsx │ │ └── sponsor │ │ │ ├── [id] │ │ │ ├── opengraph-image.tsx │ │ │ └── page.tsx │ │ │ └── components │ │ │ ├── ApprovalProposalAction.tsx │ │ │ ├── BasicProposalAction.tsx │ │ │ ├── OptimisticProposalAction.tsx │ │ │ ├── SocialProposalAction.tsx │ │ │ ├── SponsorActions.tsx │ │ │ ├── SponsorAuthCheck.tsx │ │ │ └── SponsorForm.tsx │ ├── retropgf │ │ ├── 3 │ │ │ ├── application │ │ │ │ └── [id] │ │ │ │ │ └── page.tsx │ │ │ ├── page.tsx │ │ │ └── summary │ │ │ │ └── page.tsx │ │ └── actions.ts │ └── staking │ │ ├── [addressOrENSName] │ │ ├── deposits │ │ │ ├── Deposit.tsx │ │ │ ├── DepositList.tsx │ │ │ └── DepositListAction.tsx │ │ └── page.jsx │ │ ├── components │ │ ├── Breadcrumbs.tsx │ │ ├── PanelClaimRewards.tsx │ │ ├── PanelNewDeposit.tsx │ │ ├── PanelSetAllowance.tsx │ │ ├── PanelSetStakeAmount.tsx │ │ ├── RedirectAfterSuccess.tsx │ │ ├── RedirectOrConnect.tsx │ │ ├── StakingFaq.tsx │ │ ├── StakingIntro.tsx │ │ ├── StakingStats.tsx │ │ ├── delegates │ │ │ ├── DelegateCard.tsx │ │ │ ├── DelegateCardList.tsx │ │ │ └── DelegateProfileImage.tsx │ │ └── receipt │ │ │ ├── Receipt.tsx │ │ │ └── ReceiptContainer.tsx │ │ ├── deposits │ │ └── [deposit_id] │ │ │ ├── components │ │ │ ├── EditDepositAmount.tsx │ │ │ └── EditDepositConfirm.tsx │ │ │ ├── delegate │ │ │ ├── components │ │ │ │ ├── EditDelegateConfirm.tsx │ │ │ │ └── EditDelegateFlow.tsx │ │ │ └── page.tsx │ │ │ └── page.tsx │ │ ├── new │ │ ├── components │ │ │ ├── NewStakeConfirm.tsx │ │ │ └── NewStakeFlow.tsx │ │ └── page.tsx │ │ └── page.tsx ├── assets │ ├── agora_logo.svg │ ├── icons │ │ ├── anonNoun.svg │ │ ├── arrow_right.svg │ │ ├── ballot.svg │ │ ├── calendar.svg │ │ ├── cardView.svg │ │ ├── checkCircleBroken.svg │ │ ├── coins.svg │ │ ├── community.svg │ │ ├── contractVoter.svg │ │ ├── currency.svg │ │ ├── delegateAvatar.svg │ │ ├── discord.svg │ │ ├── expand.svg │ │ ├── file.svg │ │ ├── flag.svg │ │ ├── icons.js │ │ ├── info.svg │ │ ├── lightbulb.svg │ │ ├── link.svg │ │ ├── liquid.svg │ │ ├── logout.svg │ │ ├── measure.svg │ │ ├── northEast.svg │ │ ├── notificationMessage.svg │ │ ├── pedestrian.svg │ │ ├── piggyBank.svg │ │ ├── power.svg │ │ ├── speakerCone.svg │ │ ├── spinner.svg │ │ ├── tableView.svg │ │ ├── users.svg │ │ ├── vote.svg │ │ ├── wallet.svg │ │ └── walletConnected.svg │ ├── optimism │ │ ├── agora-sticker.svg │ │ ├── agora_optimism_og_image.png │ │ ├── op-logo.svg │ │ ├── op-token-sticker.svg │ │ ├── optimism_hero_background.png │ │ ├── spark1-sticker.svg │ │ ├── spark2-sticker.svg │ │ ├── sunny-bg-sticker.svg │ │ ├── sunny-face-sticker.svg │ │ └── thumb-sticker.svg │ └── tenant │ │ ├── agora.svg │ │ ├── b3_delegate.svg │ │ ├── b3_info_1.png │ │ ├── b3_info_2.png │ │ ├── b3_info_3.png │ │ ├── b3_info_4.png │ │ ├── b3_info_hero.png │ │ ├── b3_logo.svg │ │ ├── b3_pending.svg │ │ ├── b3_success.svg │ │ ├── boost_banner.png │ │ ├── boost_hero.png │ │ ├── boost_info_1.png │ │ ├── boost_info_2.png │ │ ├── boost_info_3.png │ │ ├── boost_info_4.png │ │ ├── boost_logo.svg │ │ ├── cyber_delegate.svg │ │ ├── cyber_hero.svg │ │ ├── cyber_info_1.png │ │ ├── cyber_info_2.png │ │ ├── cyber_info_3.png │ │ ├── cyber_info_4.png │ │ ├── cyber_info_hero.png │ │ ├── cyber_logo.svg │ │ ├── cyber_pending.svg │ │ ├── cyber_success.svg │ │ ├── demo_delegate.svg │ │ ├── demo_discord.png │ │ ├── demo_docs.png │ │ ├── demo_forum.png │ │ ├── demo_hero.png │ │ ├── demo_hero_v2.svg │ │ ├── demo_logo.svg │ │ ├── demo_vision.png │ │ ├── derive_delegate.svg │ │ ├── derive_gov.png │ │ ├── derive_hero.svg │ │ ├── derive_info_0.png │ │ ├── derive_info_1.png │ │ ├── derive_info_2.png │ │ ├── derive_info_3.png │ │ ├── derive_logo.svg │ │ ├── derive_pending.svg │ │ ├── derive_success.svg │ │ ├── ens_hero.svg │ │ ├── ens_info_0.png │ │ ├── ens_info_1.png │ │ ├── ens_info_2.png │ │ ├── ens_info_hero.png │ │ ├── ens_logo.svg │ │ ├── ens_pending.svg │ │ ├── ens_success.svg │ │ ├── etherfi_delegate.svg │ │ ├── etherfi_hero.svg │ │ ├── etherfi_logo.svg │ │ ├── etherfi_pending.svg │ │ ├── etherfi_success.svg │ │ ├── linea_delegate.svg │ │ ├── linea_info_1.png │ │ ├── linea_info_2.png │ │ ├── linea_info_3.png │ │ ├── linea_info_4.png │ │ ├── linea_info_hero.png │ │ ├── linea_logo.svg │ │ ├── optimism_delegate.svg │ │ ├── optimism_hero.svg │ │ ├── optimism_info_1.png │ │ ├── optimism_info_2.png │ │ ├── optimism_info_3.png │ │ ├── optimism_info_4.png │ │ ├── optimism_info_hero.png │ │ ├── optimism_logo.svg │ │ ├── optimism_pending.svg │ │ ├── optimism_success.svg │ │ ├── pguild_hero.svg │ │ ├── pguild_logo.svg │ │ ├── scroll_delegate.svg │ │ ├── scroll_failed.svg │ │ ├── scroll_favicon │ │ ├── apple-touch-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ └── favicon.ico │ │ ├── scroll_gov.png │ │ ├── scroll_hero.png │ │ ├── scroll_hero.svg │ │ ├── scroll_info_1.png │ │ ├── scroll_info_2.png │ │ ├── scroll_info_3.png │ │ ├── scroll_info_4.png │ │ ├── scroll_info_hero.png │ │ ├── scroll_logo.svg │ │ ├── scroll_logo_old.svg │ │ ├── scroll_pending.svg │ │ ├── scroll_success.svg │ │ ├── uniswap_delegate.svg │ │ ├── uniswap_hero.svg │ │ ├── uniswap_info_1.png │ │ ├── uniswap_info_2.png │ │ ├── uniswap_info_3.png │ │ ├── uniswap_info_4.png │ │ ├── uniswap_info_hero.png │ │ ├── uniswap_logo.svg │ │ ├── uniswap_pending.svg │ │ ├── uniswap_staking_deposit.svg │ │ ├── uniswap_staking_rewards.svg │ │ ├── uniswap_success.svg │ │ ├── xai_failed.svg │ │ ├── xai_hero.svg │ │ ├── xai_info_1.svg │ │ ├── xai_info_2.svg │ │ ├── xai_info_3.svg │ │ ├── xai_info_4.svg │ │ ├── xai_info_page_hero.svg │ │ ├── xai_logo.svg │ │ ├── xai_pending.svg │ │ └── xai_success.svg ├── components │ ├── Admin │ │ ├── AdminAccountActions.tsx │ │ ├── AdminForm.tsx │ │ ├── CreateAccountActionDialog.tsx │ │ ├── CreateScopeDialog.tsx │ │ ├── FAQs.tsx │ │ ├── GovernorSettings.tsx │ │ ├── ProposalType.tsx │ │ ├── ProposalTypeSettings.tsx │ │ └── ScopeDetails.tsx │ ├── Button.jsx │ ├── Button.tsx │ ├── Changelog │ │ ├── ChangelogList.tsx │ │ └── ChangelogListEntry.tsx │ ├── Container.jsx │ ├── DelegateStatement │ │ ├── CurrentDelegateStatement.tsx │ │ ├── DelegateStatementBoolSelector.tsx │ │ ├── DelegateStatementForm.tsx │ │ ├── DelegateStatementFormSection.tsx │ │ ├── DelegateStatementInputGroup.tsx │ │ ├── NotificationSelector.tsx │ │ ├── OtherInfoFormSection.tsx │ │ ├── TopIssuesFormSection.tsx │ │ └── TopStakeholdersFormSection.tsx │ ├── Delegates │ │ ├── DelegateCard │ │ │ ├── AdvancedDelegateButton.tsx │ │ │ ├── DelegateActions.tsx │ │ │ ├── DelegateButton.tsx │ │ │ ├── DelegateCard.tsx │ │ │ ├── DelegateCardClient.tsx │ │ │ ├── DelegateCardEditProfile.tsx │ │ │ ├── DelegateCardHeader.tsx │ │ │ ├── DelegateProfileImage.tsx │ │ │ ├── DelegateSocialLinks.jsx │ │ │ ├── DelegationSelector.tsx │ │ │ ├── PartialDelegateButton.tsx │ │ │ ├── SCWProfileImage.tsx │ │ │ ├── UndelegateButton.tsx │ │ │ └── __tests__ │ │ │ │ └── DelegateProfileImageWithMetadata.test.tsx │ │ ├── DelegateCardList │ │ │ ├── CitzenCardList.tsx │ │ │ ├── DelegateCard.tsx │ │ │ ├── DelegateCardList.tsx │ │ │ ├── DelegateCardWrapper.tsx │ │ │ ├── DelegateContent.tsx │ │ │ ├── DelegateTable.tsx │ │ │ ├── DelegateTableRow.tsx │ │ │ ├── DelegateToSelfBanner.tsx │ │ │ └── delegateUtils.ts │ │ ├── DelegateStatement │ │ │ ├── DelegateStatement.jsx │ │ │ ├── DelegateStatementContainer.tsx │ │ │ ├── DelegateStatementWrapper.tsx │ │ │ ├── TopIssues.tsx │ │ │ └── TopStakeholders.tsx │ │ ├── DelegateVotes │ │ │ ├── ApprovalVoteReason.tsx │ │ │ ├── DelegateVoteIcon.tsx │ │ │ ├── DelegateVotes.tsx │ │ │ ├── DelegateVotesDetailsContainer.tsx │ │ │ ├── DelegateVotesReason.tsx │ │ │ ├── SnapshotVotes.tsx │ │ │ ├── VotesContainer.tsx │ │ │ └── VotesContainerWrapper.tsx │ │ ├── DelegatesFilter │ │ │ ├── DelegateFilterCheckBoxItem.tsx │ │ │ ├── DelegatesFilter.tsx │ │ │ ├── DelegatesIssuesFilter.tsx │ │ │ ├── DelegatesSortFilter.tsx │ │ │ ├── DelegatesStakeholdersFilter.tsx │ │ │ ├── FilterSortOption.tsx │ │ │ ├── MobileDelegatesFilter.tsx │ │ │ ├── __tests__ │ │ │ │ ├── DelegateFilterCheckBoxItem.test.tsx │ │ │ │ ├── DelegatesFilter.test.tsx │ │ │ │ ├── DelegatesSortFilter.test.tsx │ │ │ │ ├── FilterSortOption.test.tsx │ │ │ │ ├── useCitizensSort.test.ts │ │ │ │ ├── useDelegateSort.test.ts │ │ │ │ └── useDelegatesFilter.test.ts │ │ │ ├── useCitizensSort.ts │ │ │ ├── useDelegatesFilter.ts │ │ │ └── useDelegatesSort.ts │ │ ├── DelegatesTabs │ │ │ ├── DelegatesFilterChips.tsx │ │ │ ├── DelegatesSearch.tsx │ │ │ ├── DelegatesTabs.tsx │ │ │ └── __tests__ │ │ │ │ └── DelegatesFilterChips.test.tsx │ │ └── Delegations │ │ │ ├── DelegateToSelf.tsx │ │ │ ├── DelegationFromRow.tsx │ │ │ ├── DelegationToRow.tsx │ │ │ ├── DelegationsContainer.tsx │ │ │ ├── DelegationsContainerWrapper.tsx │ │ │ └── EncourageConnectWalletDialog.tsx │ ├── DevBanner.jsx │ ├── Dialogs │ │ ├── AdvancedDelegateDialog │ │ │ ├── AdvancedDelegateDialog.tsx │ │ │ ├── AdvancedDelegationDisplayAmount.tsx │ │ │ ├── SubdelegationRow.tsx │ │ │ ├── SuccessView.tsx │ │ │ └── useAdvancedDelegation.tsx │ │ ├── DelegateDialog │ │ │ ├── DelegateButton.tsx │ │ │ ├── DelegateDialog.tsx │ │ │ └── DelegationDisplayAmount.tsx │ │ ├── DialogProvider │ │ │ ├── DialogProvider.tsx │ │ │ ├── dialogs.tsx │ │ │ └── types.ts │ │ ├── PartialDelegateDialog │ │ │ ├── PartialDelegationButton.tsx │ │ │ ├── PartialDelegationDialog.tsx │ │ │ ├── PartialDelegationEntry.tsx │ │ │ ├── PartialDelegationSuccess.tsx │ │ │ └── ScwPartialDelegationButton.tsx │ │ ├── SimulationReportDialog │ │ │ └── SimulationReportDialog.tsx │ │ ├── SwitchNetworkDialog │ │ │ └── SwitchNetworkDialog.tsx │ │ └── UndelegateDialog │ │ │ ├── DelegationDisplayAmount.tsx │ │ │ ├── UndelegateDialog.tsx │ │ │ └── revalidateAction.tsx │ ├── Events │ │ └── EventFeed.jsx │ ├── Footer.tsx │ ├── Header │ │ ├── BetaBanner.tsx │ │ ├── ConnectButton.tsx │ │ ├── DesktopConnectButton.tsx │ │ ├── DesktopProfileDropDown.tsx │ │ ├── EncourageDelegationDot.tsx │ │ ├── Header.jsx │ │ ├── HeaderLink.jsx │ │ ├── LogoLink.jsx │ │ ├── MobileConnectButton.tsx │ │ ├── MobileNavMenu.tsx │ │ ├── MobileProfileDropDown.tsx │ │ ├── Navbar.jsx │ │ ├── ProfileDropDownContent.tsx │ │ ├── SocialLinks.jsx │ │ └── header.module.scss │ ├── Hero │ │ └── Hero.jsx │ ├── Layout │ │ ├── Loader.jsx │ │ ├── PageContainer.tsx │ │ ├── PageHeader │ │ │ └── PageHeader.jsx │ │ └── Stack.tsx │ ├── Logo.jsx │ ├── Metrics │ │ └── DAOMetricsHeader.jsx │ ├── Notifications │ │ ├── DialogImage │ │ │ ├── EnvelopeBottom.tsx │ │ │ ├── EnvelopePaper.tsx │ │ │ ├── EnvelopeTop.tsx │ │ │ └── Star.tsx │ │ ├── SubscribeDialog.tsx │ │ └── SubscribeDialogRootLauncher.tsx │ ├── Proposals │ │ ├── CurrentGovernanceStage │ │ │ └── CurrentGovernanceStage.tsx │ │ ├── DraftProposals │ │ │ ├── DraftProposalCard.tsx │ │ │ ├── MyDraftProposals.tsx │ │ │ └── MySponsorshipRequests.tsx │ │ ├── NeedsMyVoteProposalsList │ │ │ ├── NeedsMyVoteProposalsList.jsx │ │ │ └── needsMyVoteProposalLists.module.scss │ │ ├── Proposal │ │ │ ├── OPApprovalProposalStatus.jsx │ │ │ ├── OPOptimisticProposalStatus.jsx │ │ │ ├── OPStandardProposalStatus.jsx │ │ │ ├── Proposal.tsx │ │ │ ├── ProposalTimeStatus.jsx │ │ │ └── SnapshotProposalStatus.tsx │ │ ├── ProposalCreation │ │ │ ├── AddTransactionsDetails.tsx │ │ │ ├── ApprovalCriteriaRow.tsx │ │ │ ├── ApprovalOptionsRow.tsx │ │ │ ├── CastProposalDialog.tsx │ │ │ ├── CreateProposalForm.tsx │ │ │ ├── InfoPanel.jsx │ │ │ ├── LabelWithInfo.tsx │ │ │ ├── ProposalTypeRow.tsx │ │ │ ├── StandardForm.tsx │ │ │ ├── SubmitButton.tsx │ │ │ └── TitleDescriptionRow.tsx │ │ ├── ProposalPage │ │ │ ├── ApprovalCastVoteDialog │ │ │ │ └── ApprovalCastVoteDialog.tsx │ │ │ ├── ApprovedTransactions │ │ │ │ ├── ApprovedTransactions.jsx │ │ │ │ ├── CodeChange.jsx │ │ │ │ └── ProposalTransactionDisplay.tsx │ │ │ ├── BubbleChart │ │ │ │ └── BubbleChart.tsx │ │ │ ├── CastVoteDialog │ │ │ │ └── CastVoteDialog.tsx │ │ │ ├── Charts │ │ │ │ └── TimelineChart.tsx │ │ │ ├── CopelandProposalPage │ │ │ │ ├── CopelandProposalCriteria │ │ │ │ │ └── CopelandProposalCriteria.tsx │ │ │ │ ├── CopelandProposalPage.tsx │ │ │ │ ├── CopelandVotesPanel │ │ │ │ │ └── CopelandVotesPanel.tsx │ │ │ │ └── OptionsResultsPanel │ │ │ │ │ └── OptionsResultsPanel.tsx │ │ │ ├── OPProposalApprovalPage │ │ │ │ ├── ApprovalProposalCriteria │ │ │ │ │ └── ApprovalProposalCriteria.jsx │ │ │ │ ├── ApprovalVotesPanel │ │ │ │ │ └── ApprovalVotesPanel.tsx │ │ │ │ ├── OPProposalApprovalPage.tsx │ │ │ │ └── OptionResultsPanel │ │ │ │ │ └── OptionResultsPanel.tsx │ │ │ ├── OPProposalPage │ │ │ │ ├── OPProposalOptimisticPage.jsx │ │ │ │ ├── ProposalVotesBar │ │ │ │ │ ├── ProposalVotesBar.jsx │ │ │ │ │ └── ProposalVotesBar.tsx │ │ │ │ ├── ProposalVotesCard │ │ │ │ │ ├── OptimisticProposalVotesCard.tsx │ │ │ │ │ ├── ProposalVotesCard.tsx │ │ │ │ │ └── ProposalVotesFilter.tsx │ │ │ │ ├── ProposalVotesSummary │ │ │ │ │ └── ProposalVotesSummary.tsx │ │ │ │ ├── ProposalVotesSummaryDetails │ │ │ │ │ └── ProposalVotesSummaryDetails.tsx │ │ │ │ └── StandardProposalPage.tsx │ │ │ ├── ProposalChart │ │ │ │ └── ProposalChart.tsx │ │ │ ├── ProposalDescription │ │ │ │ ├── ProposalDescription.tsx │ │ │ │ └── proposalDescription.module.scss │ │ │ ├── ProposalTitle │ │ │ │ └── ProposalTitle.tsx │ │ │ ├── ShareVoteDialog │ │ │ │ ├── ShareVoteDialog.tsx │ │ │ │ └── TenantLogo.tsx │ │ │ └── TreeMapChart │ │ │ │ └── TreeMapChart.tsx │ │ ├── ProposalStatus │ │ │ ├── ProposalStatus.jsx │ │ │ └── ProposalStatusDetail.tsx │ │ ├── ProposalsFilter │ │ │ └── ProposalsFilter.tsx │ │ ├── ProposalsHome.tsx │ │ └── ProposalsList │ │ │ ├── CreateProposalDraftButton.tsx │ │ │ ├── ProposalsList.tsx │ │ │ ├── __tests__ │ │ │ └── CreateProposalDraftButton.test.tsx │ │ │ └── actions │ │ │ └── createProposalDraft.tsx │ ├── RetroPGF │ │ ├── RetroPGFApplicationBanner.tsx │ │ ├── RetroPGFApplicationContent.tsx │ │ ├── RetroPGFApplicationContentFundingSource.tsx │ │ ├── RetroPGFCategoryFilter.tsx │ │ ├── RetroPGFFilters.tsx │ │ ├── RetroPGFHero.tsx │ │ ├── RetroPGFResults.tsx │ │ ├── RetroPGFSearch.tsx │ │ ├── RetroPGFShareCardDialog.tsx │ │ └── RetroPGFSort.tsx │ ├── Simulation │ │ └── StructuredReport.tsx │ ├── Votes │ │ ├── ApprovalCastVoteButton │ │ │ └── ApprovalCastVoteButton.tsx │ │ ├── ApprovalProposalVotesList │ │ │ ├── ApprovalProposalSingleVote.tsx │ │ │ └── ApprovalProposalVotesList.tsx │ │ ├── CastVoteInput │ │ │ ├── CastVoteContext.test.tsx │ │ │ ├── CastVoteContext.tsx │ │ │ └── CastVoteInput.tsx │ │ ├── CopelandProposalVotesList │ │ │ ├── CopelandProposalSingleVote.tsx │ │ │ └── CopelandProposalVotesList.tsx │ │ └── ProposalVotesList │ │ │ ├── ProposalNonVoterList.tsx │ │ │ ├── ProposalSingleNonVoter.tsx │ │ │ ├── ProposalSingleVote.tsx │ │ │ └── ProposalVotesList.tsx │ ├── common │ │ ├── CountBadge.tsx │ │ └── FilterResetListbox.tsx │ ├── shared │ │ ├── AgoraLoader │ │ │ └── AgoraLoader.tsx │ │ ├── BlockScanUrl.tsx │ │ ├── CloseIcon.tsx │ │ ├── CollapsibleText.tsx │ │ ├── CopyableHumanAddress.tsx │ │ ├── ENSAvatar.tsx │ │ ├── ENSName.tsx │ │ ├── Form │ │ │ └── TextInputWithTooltip.tsx │ │ ├── HumanVote.jsx │ │ ├── InfoPop.tsx │ │ ├── InputBox.tsx │ │ ├── LoadingSpinner.tsx │ │ ├── Markdown │ │ │ ├── Markdown.tsx │ │ │ └── markdown.module.scss │ │ ├── MultiButtons.tsx │ │ ├── ResourceNotFound │ │ │ └── ResourceNotFound.tsx │ │ ├── RouteNotSupported.tsx │ │ ├── SimulateTransaction.tsx │ │ ├── SiweProviderConfig.tsx │ │ ├── Switch.tsx │ │ ├── TokenAmount.tsx │ │ ├── TokenAmountDecorated.tsx │ │ ├── TruncatedAddress.jsx │ │ └── avatars │ │ │ ├── avatar0-1.svg │ │ │ ├── avatar0.svg │ │ │ ├── avatar1.svg │ │ │ ├── avatar2.svg │ │ │ ├── avatar3.svg │ │ │ ├── avatar4.svg │ │ │ ├── avatar5.svg │ │ │ ├── avatar6.svg │ │ │ └── avatar7.svg │ └── ui │ │ ├── CheckboxWithTitle │ │ └── CheckboxWithTitle.tsx │ │ ├── Drawer.tsx │ │ ├── accordion.tsx │ │ ├── button.tsx │ │ ├── card.tsx │ │ ├── checkbox.tsx │ │ ├── dropdown-menu.tsx │ │ ├── form.tsx │ │ ├── hover-card.tsx │ │ ├── input.tsx │ │ ├── label.tsx │ │ ├── popover.tsx │ │ ├── select.tsx │ │ ├── separator.tsx │ │ ├── table.tsx │ │ ├── tabs.tsx │ │ ├── textarea.tsx │ │ └── tooltip.tsx ├── contexts │ ├── AgoraContext.tsx │ └── ConnectButtonContext.tsx ├── hooks │ ├── index.ts │ ├── useAddSearchParam.ts │ ├── useAdvancedVoting.tsx │ ├── useCalculateCopelandResult.tsx │ ├── useConnectedDelegate.ts │ ├── useContractAbi.ts │ ├── useDAOMetrics.js │ ├── useDelegate.tsx │ ├── useDeleteSearchParam.ts │ ├── useDepositorTotalStaked.ts │ ├── useEffectEvent.tsx │ ├── useEthBalance.ts │ ├── useFetchAllForVoting.ts │ ├── useGetDelegatee.ts │ ├── useGetVotableSupply.ts │ ├── useGetVotes.ts │ ├── useGovernorAdmin.ts │ ├── useGovernorName.ts │ ├── useLatestBlock.ts │ ├── useManager.ts │ ├── useNonce.ts │ ├── useProfileData.ts │ ├── useProposalNonVotes.ts │ ├── useProposalThreshold.ts │ ├── useProposalVotes.ts │ ├── useProposalVotesChart.ts │ ├── useRewardDuration.ts │ ├── useRewardPerToken.ts │ ├── useScwVoting.tsx │ ├── useSmartAccountAddress.ts │ ├── useSmartAccountAlchemy.ts │ ├── useSmartAccountDerive.ts │ ├── useSponsoredDelegation.ts │ ├── useSponsoredVoting.tsx │ ├── useStandardVoting.tsx │ ├── useTenantColorScheme.tsx │ ├── useTokenAllowance.ts │ ├── useTokenBalance.ts │ ├── useTokenName.ts │ ├── useTotalStaked.ts │ ├── useTotalSupply.ts │ ├── useTransactionDecoding.ts │ ├── useVotableSupply.ts │ └── useVoterStats.ts ├── icons │ ├── AgoraIcon.tsx │ ├── AgoraIconWithText.tsx │ ├── AgoraLoader.tsx │ ├── ArrowRight.tsx │ ├── CheckCircleBrokenIcon.tsx │ ├── CheckMark.tsx │ ├── ClipboardIcon.tsx │ ├── CoinsIcon.tsx │ ├── CubeIcon.tsx │ ├── ExclamationCircleIcon.tsx │ ├── ExpandCollapseIcon.tsx │ ├── GridLayoutIcon.tsx │ ├── HamburgerIcon.tsx │ ├── InfoIcon.tsx │ ├── ListViewIcon.tsx │ ├── NotificationIcon.tsx │ ├── PowerIcon.tsx │ ├── Sort.tsx │ ├── agoraIconWithText.svg │ ├── agoraLoaderDark.gif │ ├── agoraLoaderLight.gif │ ├── anonNoun.svg │ ├── badge.svg │ ├── ballot.svg │ ├── banknotes.svg │ ├── beaker.svg │ ├── block.svg │ ├── cardView.svg │ ├── chatBubble.svg │ ├── check.svg │ ├── chevronSelectorVertical.svg │ ├── clipboard.svg │ ├── closeIcon.tsx │ ├── codeBracketSquare.svg │ ├── community.svg │ ├── contractVoter.svg │ ├── copy.svg │ ├── discord.svg │ ├── emoji │ │ ├── balance-scale.png │ │ ├── hammer.png │ │ ├── lock.png │ │ ├── people.png │ │ ├── shield.png │ │ ├── sprout.png │ │ ├── test-tube.png │ │ └── world.png │ ├── endorsed.svg │ ├── expand.svg │ ├── farcaster.svg │ ├── filter.tsx │ ├── freeGasMegaphon.gif │ ├── github.svg │ ├── globe.svg │ ├── globeAlt.svg │ ├── icons.ts │ ├── info-transparent.svg │ ├── info.svg │ ├── infoRed.svg │ ├── link.svg │ ├── liquid.svg │ ├── lockClosed.svg │ ├── logout.tsx │ ├── measure.svg │ ├── pedestrian.svg │ ├── piggyBank.svg │ ├── power.svg │ ├── presentationChartLine.svg │ ├── profile.svg │ ├── projectPlaceholder.svg │ ├── scale.svg │ ├── search.svg │ ├── share.svg │ ├── shareCard.svg │ ├── shieldCheck.svg │ ├── sparks.svg │ ├── speakerCone.svg │ ├── spinner.svg │ ├── sponsor.svg │ ├── stack.svg │ ├── sunny.svg │ ├── tableView.svg │ ├── tokenIcon.svg │ ├── vote.svg │ ├── wallet.svg │ ├── walletConnected.svg │ ├── walletIcon.tsx │ ├── warpcast.svg │ ├── world.svg │ ├── wrenchScredriver.svg │ └── x.svg ├── instrumentation.node.ts ├── instrumentation.ts ├── lib │ ├── __tests__ │ │ ├── analytics.test.ts │ │ ├── copelandCalculation.test.ts │ │ ├── utils.test.ts │ │ └── voteUtils.test.ts │ ├── abiUtils.ts │ ├── alligatorUtils.ts │ ├── analytics.ts │ ├── bigintUtils.ts │ ├── blockTimes.ts │ ├── constants.ts │ ├── contracts │ │ ├── abis │ │ │ ├── AgoraGovernor.json │ │ │ ├── AgoraGovernor_1.1.json │ │ │ ├── AgoraTimelock.json │ │ │ ├── AgoraToken.json │ │ │ ├── AlligatorOPV5.json │ │ │ ├── ApprovalVotingModule.json │ │ │ ├── CyberProposalTypes.json │ │ │ ├── ENSGovernor.json │ │ │ ├── ENSTimelock.json │ │ │ ├── ERC20.json │ │ │ ├── LightAccountFactory.ts │ │ │ ├── Membership.json │ │ │ ├── NounsGovernor.json │ │ │ ├── OptimismGovernor.json │ │ │ ├── ProposalTypesConfigurator.json │ │ │ ├── ProposalTypesConfiguratorScopes.json │ │ │ ├── UniswapGovernor.json │ │ │ ├── UniswapStaker.json │ │ │ ├── UniswapTimelock.json │ │ │ ├── UniswapToken.json │ │ │ └── VotableSupplyOracle.json │ │ ├── common │ │ │ └── interfaces │ │ │ │ ├── IAlligatorContract.ts │ │ │ │ ├── IGovernorContract.ts │ │ │ │ ├── IMembershipContract.ts │ │ │ │ ├── IStaker.ts │ │ │ │ ├── ITimelockContract.ts │ │ │ │ ├── ITokenContract.ts │ │ │ │ └── IVotableSupplyOracleContract.ts │ │ ├── contracts.ts │ │ └── generated │ │ │ ├── UniswapStaker.ts │ │ │ ├── factories │ │ │ ├── UniswapStaker__factory.ts │ │ │ └── index.ts │ │ │ └── index.ts │ ├── copelandCalculation.ts │ ├── metricWrapper.ts │ ├── monitoringService.ts │ ├── prismaUtils.ts │ ├── proposalUtils.ts │ ├── sanitizationUtils.ts │ ├── seatbelt │ │ ├── checkProposal.ts │ │ ├── checks │ │ │ ├── check-decode-calldata.ts │ │ │ ├── check-eth-balance-changes.ts │ │ │ ├── check-logs.ts │ │ │ ├── check-state-changes.ts │ │ │ ├── check-targets-no-selfdestruct.ts │ │ │ ├── check-targets-verified-etherscan.ts │ │ │ ├── check-value-required.ts │ │ │ └── index.ts │ │ ├── encode-state.ts │ │ ├── report.ts │ │ ├── simulate.ts │ │ └── types.ts │ ├── serverVerifyMessage.ts │ ├── tenant │ │ ├── configs │ │ │ ├── contracts │ │ │ │ ├── b3.ts │ │ │ │ ├── boost.ts │ │ │ │ ├── cyber.ts │ │ │ │ ├── demo.ts │ │ │ │ ├── derive.ts │ │ │ │ ├── ens.ts │ │ │ │ ├── etherfi.ts │ │ │ │ ├── linea.ts │ │ │ │ ├── optimism.ts │ │ │ │ ├── protocol-guild.ts │ │ │ │ ├── scroll.ts │ │ │ │ ├── uniswap.ts │ │ │ │ └── xai.ts │ │ │ └── ui │ │ │ │ ├── b3.ts │ │ │ │ ├── boost.ts │ │ │ │ ├── cyber.ts │ │ │ │ ├── demo.ts │ │ │ │ ├── derive.ts │ │ │ │ ├── ens.ts │ │ │ │ ├── etherfi.ts │ │ │ │ ├── linea.ts │ │ │ │ ├── optimism.ts │ │ │ │ ├── protocol-guild.ts │ │ │ │ ├── scroll.ts │ │ │ │ ├── uniswap.ts │ │ │ │ └── xai.ts │ │ ├── tenant.ts │ │ ├── tenantContract.ts │ │ ├── tenantContractFactory.ts │ │ ├── tenantSlugFactory.ts │ │ ├── tenantTokenFactory.ts │ │ ├── tenantUI.ts │ │ └── tenantUIFactory.ts │ ├── tokenUtils.ts │ ├── transactionDecoder.ts │ ├── types.d.ts │ ├── utils.ts │ ├── viem.ts │ └── voteUtils.ts ├── middleware.ts ├── pages │ ├── _error.jsx │ └── api_v1 │ │ └── index.tsx ├── scripts │ ├── dataDogAlerts.js │ ├── generateApiKey.mjs │ ├── generateJwt.ts │ ├── uploadDelegateStatements.ts │ ├── uploadProjectsData.js │ ├── uploadR6MockProjectsData.js │ ├── uploadRFMetricsData.js │ └── uploadRetroFundingMetrcis.js ├── stores │ └── delegateStatement.ts └── styles │ ├── fonts.ts │ ├── globals.scss │ ├── theme.js │ └── variables.scss ├── sweep.yaml ├── tailwind.config.js ├── tsconfig.json ├── vitest.config.mts └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .vscode 3 | node_modules 4 | *.log 5 | .env 6 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/action-jira-linker.yml: -------------------------------------------------------------------------------- 1 | name: action-jira-linker 2 | on: [pull_request] 3 | 4 | jobs: 5 | action-jira-linter: 6 | permissions: write-all 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: exogee-technology/action-jira-linker@v1.0.0 10 | with: 11 | github-token: ${{ secrets.GITHUB_TOKEN }} 12 | jira-user: ${{ secrets.JIRA_USER }} 13 | jira-token: ${{ secrets.JIRA_TOKEN }} 14 | jira-base-url: https://voteagora.atlassian.net 15 | comment-header: | 16 | ## JIRA Information 17 | comment-trailer: | 18 | Fix the governance, fix the world. 19 | fail-on-error: true 20 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | run-name: ${{ github.actor }} - ${{ github.event_name }} - ${{ github.repository }} 3 | on: 4 | pull_request: 5 | branches: 6 | - 'main' 7 | push: 8 | branches: 9 | - 'main' 10 | 11 | jobs: 12 | # swagger_lint: 13 | # runs-on: ubuntu-latest 14 | # name: Swagger Editor Validator Remote 15 | # 16 | # steps: 17 | # - uses: actions/checkout@v2 18 | # - name: Validate OpenAPI definition 19 | # uses: char0n/apidom-validate@v1 20 | # with: 21 | # fails-on: 2 22 | # definition-file: spec/oas_v1.yaml 23 | 24 | 25 | lint-prettier: 26 | runs-on: ubuntu-latest 27 | steps: 28 | - uses: actions/checkout@v2 29 | - name: Install dependencies 30 | run: yarn install 31 | - name: Lint with Prettier 32 | run: yarn check-prettier 33 | -------------------------------------------------------------------------------- /.github/workflows/unit-tests.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | on: 3 | push: 4 | branches: [main] 5 | pull_request: 6 | types: [opened, synchronize] 7 | 8 | jobs: 9 | unit-tests: 10 | runs-on: ubuntu-latest 11 | 12 | env: 13 | READ_WRITE_WEB2_DATABASE_URL_DEV: ${{ secrets.READ_WRITE_WEB2_DATABASE_URL_DEV }} 14 | READ_ONLY_WEB3_DATABASE_URL_DEV: ${{ secrets.READ_ONLY_WEB3_DATABASE_URL_DEV }} 15 | NEXT_PUBLIC_AGORA_ENV: "dev" 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: "20" 24 | cache: "yarn" 25 | 26 | - name: Install dependencies 27 | run: yarn install --frozen-lockfile 28 | 29 | - name: Generate Typechain 30 | run: yarn generate-typechain 31 | 32 | - name: Run unit tests 33 | run: yarn test 34 | -------------------------------------------------------------------------------- /.github/workflows/vercel-preview-maker.yml: -------------------------------------------------------------------------------- 1 | name: PR Comment Preview Build (Python) 2 | 3 | on: 4 | issue_comment: 5 | types: [created] 6 | 7 | jobs: 8 | preview-build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Check out repository 13 | uses: actions/checkout@v3 14 | 15 | - name: Set up Python 16 | uses: actions/setup-python@v4 17 | with: 18 | python-version: '3.11' # or whichever version you prefer 19 | 20 | - name: Install Python dependencies 21 | run: pip install requests 22 | 23 | - name: Run preview script 24 | # We'll pass some data via environment variables and 25 | # rely on GitHub's default "event payload" file for more details. 26 | run: python .github/scripts/preview.py 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} 30 | GITHUB_EVENT_PATH: ${{ github.event_path }} # the path to the event payload (JSON) 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | .idea 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | .env* 31 | .env 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | next-env.d.ts 39 | 40 | 41 | # prisma views 42 | /prisma/views 43 | 44 | # Sentry Config File 45 | .sentryclirc 46 | 47 | # Local prettier config 48 | .prettierrc 49 | 50 | /src/lib/contracts/generated 51 | 52 | dist 53 | 54 | # version manager 55 | .tool-versions 56 | -------------------------------------------------------------------------------- /.hintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "development" 4 | ], 5 | "hints": { 6 | "compat-api/css": [ 7 | "default", 8 | { 9 | "ignore": [ 10 | "@layer", 11 | "width: fit-content", 12 | "@layer" 13 | ] 14 | } 15 | ] 16 | } 17 | } -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "trailingComma": "es5", 4 | "semi": true 5 | } 6 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | nodejs 20.0.0 2 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "chrome", 9 | "request": "launch", 10 | "name": "Launch Chrome against localhost", 11 | "url": "http://localhost:3000", 12 | "webRoot": "${workspaceFolder}" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "esbenp.prettier-vscode", 3 | "editor.formatOnSave": true, 4 | // "editor.codeActionsOnSave": { 5 | // "source.fixAll.eslint": "explicit" 6 | // }, 7 | "tailwindCSS.experimental.classRegex": [ 8 | ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"], 9 | ["cx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"] 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /Dockerfile.base: -------------------------------------------------------------------------------- 1 | # ---------- Stage 1: base with deps ---------- 2 | FROM node:22-bullseye-slim AS base 3 | 4 | WORKDIR /app 5 | 6 | # Install OS deps only if really needed (prisma builds, etc.) 7 | RUN apt-get update && apt-get install -y \ 8 | openssl \ 9 | python3 \ 10 | build-essential \ 11 | git \ 12 | && rm -rf /var/lib/apt/lists/* 13 | 14 | # Copy only what's needed to install deps 15 | COPY package.json yarn.lock ./ 16 | RUN yarn install --frozen-lockfile 17 | 18 | -------------------------------------------------------------------------------- /apps/nouns/node_modules/tsconfig: -------------------------------------------------------------------------------- 1 | ../../../packages/tsconfig -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tailwind": { 6 | "config": "tailwind.config.js", 7 | "css": "/src/app/global.scss", 8 | "baseColor": "slate", 9 | "cssVariables": true 10 | }, 11 | "aliases": { 12 | "components": "@/components", 13 | "utils": "@/lib/utils" 14 | } 15 | } -------------------------------------------------------------------------------- /cypress.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | We use Cypress to write our end-to-end tests. 3 | Checkout https://docs.cypress.io/guides/references/configuration for more details. 4 | */ 5 | 6 | import { defineConfig } from "cypress"; 7 | 8 | export default defineConfig({ 9 | e2e: { 10 | setupNodeEvents(on, config) { 11 | // implement node event listeners here 12 | }, 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /cypress/e2e/agora.cy.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Smoke test that the app loads and the nav works. 3 | */ 4 | 5 | describe("Agora", () => { 6 | it("Nav works", () => { 7 | cy.visit("http://localhost:3000"); 8 | cy.get("nav a").should("contain", "Proposals"); 9 | cy.get("a.delegatesNav").click({ force: true }); 10 | cy.url().should("include", "/delegates"); 11 | cy.get("h1").contains("Delegates"); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /cypress/support/e2e.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/e2e.ts is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') -------------------------------------------------------------------------------- /env.sample: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_AGORA_ENV=dev 2 | NEXT_PUBLIC_ALCHEMY_ID= 3 | NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID= 4 | NEXT_PUBLIC_AGORA_API_KEY= 5 | NEXT_PUBLIC_AGORA_INSTANCE_TOKEN=OP 6 | NEXT_PUBLIC_AGORA_BASE_URL=http://localhost:3000/api/v1 7 | NEXT_PUBLIC_AGORA_INSTANCE_NAME=optimism 8 | READ_WRITE_WEB2_DATABASE_URL_PROD= 9 | READ_ONLY_WEB3_DATABASE_URL_PROD= 10 | READ_WRITE_WEB2_DATABASE_URL_DEV= 11 | READ_ONLY_WEB3_DATABASE_URL_DEV= 12 | AWS_ACCESS_KEY_ID= 13 | AWS_SECRET_ACCESS_KEY= 14 | OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 15 | TENDERLY_ACCESS_KEY= 16 | TENDERLY_USER= 17 | TENDERLY_PROJECT= 18 | NEXT_PUBLIC_ENABLE_BI_METRICS_CAPTURE= 19 | NEXT_PUBLIC_ETHERSCAN_API_KEY= 20 | DAO_NODE_URL= -------------------------------------------------------------------------------- /funding.json: -------------------------------------------------------------------------------- 1 | { 2 | "opRetro": { 3 | "projectId": "0x32942ffa0d751769bb55b53d9c32085463e9bfe668ae8aa5d4a736c46b4130c5" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/database/node_modules/tsconfig: -------------------------------------------------------------------------------- 1 | ../../tsconfig -------------------------------------------------------------------------------- /packages/ui/node_modules/tsconfig: -------------------------------------------------------------------------------- 1 | ../../tsconfig -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/.well-known/walletconnect.txt: -------------------------------------------------------------------------------- 1 | facc7599-5d45-465e-8114-fe1fb1a1d5a9=671e50125c88e7c296afad29f88557a6b16317ce90ba6aefc6971da680753291 -------------------------------------------------------------------------------- /public/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /public/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/favicon/favicon.ico -------------------------------------------------------------------------------- /public/favicon/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/favicon/mstile-144x144.png -------------------------------------------------------------------------------- /public/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /public/favicon/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/favicon/mstile-310x150.png -------------------------------------------------------------------------------- /public/favicon/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/favicon/mstile-310x310.png -------------------------------------------------------------------------------- /public/favicon/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/favicon/mstile-70x70.png -------------------------------------------------------------------------------- /public/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /public/fonts/Inter/Inter-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/fonts/Inter/Inter-Black.ttf -------------------------------------------------------------------------------- /public/fonts/Inter/Inter-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/fonts/Inter/Inter-Bold.ttf -------------------------------------------------------------------------------- /public/fonts/Inter/Inter-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/fonts/Inter/Inter-Medium.ttf -------------------------------------------------------------------------------- /public/fonts/Inter/Inter-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/fonts/Inter/Inter-Regular.ttf -------------------------------------------------------------------------------- /public/fonts/Rubik-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/fonts/Rubik-Bold.ttf -------------------------------------------------------------------------------- /public/fonts/Rubik-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/fonts/Rubik-BoldItalic.ttf -------------------------------------------------------------------------------- /public/fonts/Rubik-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/fonts/Rubik-Medium.ttf -------------------------------------------------------------------------------- /public/fonts/Rubik-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/fonts/Rubik-Regular.ttf -------------------------------------------------------------------------------- /public/fonts/TransSansPremium/TransSansPremium_Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/fonts/TransSansPremium/TransSansPremium_Bold.otf -------------------------------------------------------------------------------- /public/fonts/TransSansPremium/TransSansPremium_Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/fonts/TransSansPremium/TransSansPremium_Light.otf -------------------------------------------------------------------------------- /public/fonts/TransSansPremium/TransSansPremium_Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/fonts/TransSansPremium/TransSansPremium_Regular.otf -------------------------------------------------------------------------------- /public/fonts/TransSansPremium/TransSansPremium_SemiBold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/fonts/TransSansPremium/TransSansPremium_SemiBold.otf -------------------------------------------------------------------------------- /public/images/blink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/images/blink.gif -------------------------------------------------------------------------------- /public/images/ens_temp_check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/images/ens_temp_check.png -------------------------------------------------------------------------------- /public/images/grid-share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/images/grid-share.png -------------------------------------------------------------------------------- /public/images/og-generic-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/images/og-generic-bg.png -------------------------------------------------------------------------------- /public/rpgf/infographic_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/rpgf/infographic_2.png -------------------------------------------------------------------------------- /public/rpgf/infographic_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/rpgf/infographic_3.png -------------------------------------------------------------------------------- /public/rpgf/infographic_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/rpgf/infographic_4.png -------------------------------------------------------------------------------- /public/rpgf/infographic_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/rpgf/infographic_5.png -------------------------------------------------------------------------------- /public/rpgf/infographic_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/rpgf/infographic_6.png -------------------------------------------------------------------------------- /public/rpgf/infographic_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/rpgf/infographic_7.png -------------------------------------------------------------------------------- /public/rpgf/infographic_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/rpgf/infographic_8.png -------------------------------------------------------------------------------- /public/rpgf/infographic_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/rpgf/infographic_9.png -------------------------------------------------------------------------------- /public/rpgf/overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/public/rpgf/overlay.png -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /setupTests.ts: -------------------------------------------------------------------------------- 1 | import "@testing-library/jest-dom/vitest"; 2 | -------------------------------------------------------------------------------- /src/__mocks__/fonts.ts: -------------------------------------------------------------------------------- 1 | export const mockFonts = { 2 | Inter: () => ({ 3 | style: { fontFamily: "Inter" }, 4 | }), 5 | Rajdhani: () => ({ 6 | style: { fontFamily: "Rajdhani" }, 7 | }), 8 | Chivo_Mono: () => ({ 9 | style: { fontFamily: "ChivoMono" }, 10 | }), 11 | }; 12 | -------------------------------------------------------------------------------- /src/app/admin/actions.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { fetchProposalTypes as apiFetchPropTyposales } from "@/app/api/common/proposals/getProposals"; 4 | 5 | export async function fetchProposalTypes() { 6 | return apiFetchPropTyposales(); 7 | } 8 | -------------------------------------------------------------------------------- /src/app/admin/loading.tsx: -------------------------------------------------------------------------------- 1 | import AgoraLoader, { 2 | LogoLoader, 3 | } from "@/components/shared/AgoraLoader/AgoraLoader"; 4 | import Tenant from "@/lib/tenant/tenant"; 5 | 6 | export default function Loading() { 7 | const { ui } = Tenant.current(); 8 | const shouldHideAgoraBranding = ui.hideAgoraBranding; 9 | 10 | if (shouldHideAgoraBranding) { 11 | return ; 12 | } 13 | 14 | return ; 15 | } 16 | -------------------------------------------------------------------------------- /src/app/admin/page.tsx: -------------------------------------------------------------------------------- 1 | export const dynamic = "force-dynamic"; // needed for app and e2e 2 | 3 | import AdminForm from "@/components/Admin/AdminForm"; 4 | import { fetchVotableSupply as apiFetchVotableSupply } from "@/app/api/common/votableSupply/getVotableSupply"; 5 | import { fetchProposalTypes } from "@/app/admin/actions"; 6 | import Tenant from "@/lib/tenant/tenant"; 7 | 8 | export const revalidate = 0; 9 | 10 | async function fetchVotableSupply() { 11 | "use server"; 12 | return apiFetchVotableSupply(); 13 | } 14 | 15 | export default async function Page() { 16 | const { ui } = Tenant.current(); 17 | 18 | if (!ui.toggle("admin")) { 19 | return
Route not supported for namespace
; 20 | } 21 | 22 | const votableSupply = await fetchVotableSupply(); 23 | const proposalTypes = await fetchProposalTypes(); 24 | 25 | return ( 26 | 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /src/app/api/analytics/metric/[metric_id]/[frequency]/route.ts: -------------------------------------------------------------------------------- 1 | import { type NextRequest, NextResponse } from "next/server"; 2 | 3 | export async function GET( 4 | request: NextRequest, 5 | { params }: { params: { metric_id: string; frequency: string } } 6 | ) { 7 | const { authenticateApiUser } = await import("@/app/lib/auth/serverAuth"); 8 | const { apiFetchMetricTS } = await import("./getMetricsTS"); 9 | 10 | const authResponse = await authenticateApiUser(request); 11 | 12 | if (!authResponse.authenticated) { 13 | return new Response(authResponse.failReason, { status: 401 }); 14 | } 15 | 16 | const { metric_id, frequency } = params; 17 | 18 | try { 19 | const communityInfo = await apiFetchMetricTS(metric_id, frequency); 20 | return NextResponse.json(communityInfo); 21 | } catch (e: any) { 22 | return new Response("Internal server error: " + e.toString(), { 23 | status: 500, 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app/api/analytics/top/delegates/route.ts: -------------------------------------------------------------------------------- 1 | import { type NextRequest, NextResponse } from "next/server"; 2 | 3 | export async function GET(request: NextRequest) { 4 | const { authenticateApiUser } = await import("@/app/lib/auth/serverAuth"); 5 | const { apiFetchDelegateWeights } = await import( 6 | "@/app/api/analytics/top/delegates/getTopDelegateWeighs" 7 | ); 8 | 9 | const authResponse = await authenticateApiUser(request); 10 | 11 | if (!authResponse.authenticated) { 12 | return new Response(authResponse.failReason, { status: 401 }); 13 | } 14 | 15 | try { 16 | const weights = await apiFetchDelegateWeights(); 17 | return NextResponse.json(weights); 18 | } catch (e: any) { 19 | return new Response("Internal server error: " + e.toString(), { 20 | status: 500, 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/app/api/analytics/vote/route.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest, NextResponse } from "next/server"; 2 | 3 | export async function GET(request: NextRequest) { 4 | const { authenticateApiUser } = await import("@/app/lib/auth/serverAuth"); 5 | const { apiFetchProposalVoteCounts } = await import( 6 | "@/app/api/analytics/vote/getProposalVoteCounts" 7 | ); 8 | 9 | const authResponse = await authenticateApiUser(request); 10 | 11 | if (!authResponse.authenticated) { 12 | return new Response(authResponse.failReason, { status: 401 }); 13 | } 14 | 15 | try { 16 | const communityInfo = await apiFetchProposalVoteCounts(); 17 | return NextResponse.json(communityInfo); 18 | } catch (e: any) { 19 | return new Response("Internal server error: " + e.toString(), { 20 | status: 500, 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/app/api/balances/[frequency]/route.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest, NextResponse } from "next/server"; 2 | 3 | export async function GET( 4 | request: NextRequest, 5 | { params }: { params: { frequency: string } } 6 | ) { 7 | const { authenticateApiUser } = await import("@/app/lib/auth/serverAuth"); 8 | const { apiFetchTreasuryBalanceTS } = await import( 9 | "@/app/api/balances/[frequency]/getTreasuryBalanceTS" 10 | ); 11 | 12 | const authResponse = await authenticateApiUser(request); 13 | 14 | if (!authResponse.authenticated) { 15 | return new Response(authResponse.failReason, { status: 401 }); 16 | } 17 | 18 | try { 19 | const frequency = request.nextUrl.pathname.split("/")[3]; 20 | 21 | const communityInfo = await apiFetchTreasuryBalanceTS(frequency); 22 | return NextResponse.json(communityInfo); 23 | } catch (e: any) { 24 | return new Response("Internal server error: " + e.toString(), { 25 | status: 500, 26 | }); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/app/api/common/authority-chains/authorityChains.d.ts: -------------------------------------------------------------------------------- 1 | import { OptimismAuthorityChainsSnaps } from "@prisma/client"; 2 | 3 | export type AuthorityChainsSnaps = OptimismAuthorityChainsSnaps; 4 | 5 | export type AuthorityChainRules = { 6 | allowance: number; 7 | allowance_type: number; 8 | custom_rule: string; 9 | not_valid_after: number; 10 | not_valid_before: number; 11 | max_redelegations: number; 12 | blocks_before_vote_closes: number; 13 | }; 14 | 15 | export type AuhtorityChainsAggregate = { 16 | chains?: String[][]; 17 | rules?: Prisma.JsonValue[][]; 18 | balances?: Decimal[]; 19 | proxies?: String[]; 20 | subdelegated_share?: Decimal; 21 | subdelegated_amount?: Decimal; 22 | }; 23 | -------------------------------------------------------------------------------- /src/app/api/common/changelogs/changelog.d.ts: -------------------------------------------------------------------------------- 1 | import { DaoSlug } from "@prisma/client"; 2 | 3 | export type Changelog = { 4 | id: number; 5 | dao_slug: DaoSlug; 6 | title: string; 7 | body: string; 8 | author_address: string; 9 | created_at: Date; 10 | updated_at: Date; 11 | }; 12 | -------------------------------------------------------------------------------- /src/app/api/common/changelogs/getChangelogs.ts: -------------------------------------------------------------------------------- 1 | import { cache } from "react"; 2 | import { 3 | PaginatedResult, 4 | paginateResult, 5 | PaginationParams, 6 | } from "@/app/lib/pagination"; 7 | import { prismaWeb2Client } from "@/app/lib/prisma"; 8 | import { DaoSlug } from "@prisma/client"; 9 | import { Changelog } from "./changelog"; 10 | 11 | async function getChangelogsForDAO({ 12 | daoSlug, 13 | pagination, 14 | }: { 15 | daoSlug: DaoSlug; 16 | pagination: PaginationParams; 17 | }): Promise> { 18 | const getChangelogsQuery = async (skip: number, take: number) => { 19 | return prismaWeb2Client.changelog.findMany({ 20 | where: { 21 | dao_slug: daoSlug, 22 | }, 23 | orderBy: { 24 | created_at: "desc", 25 | }, 26 | skip, 27 | take, 28 | }); 29 | }; 30 | 31 | return await paginateResult(getChangelogsQuery, pagination); 32 | } 33 | export const fetchChangelogForDAO = cache(getChangelogsForDAO); 34 | -------------------------------------------------------------------------------- /src/app/api/common/citizens/isCitizen.ts: -------------------------------------------------------------------------------- 1 | import { Prisma } from "@prisma/client"; 2 | import { cache } from "react"; 3 | import { prismaWeb2Client } from "@/app/lib/prisma"; 4 | import Tenant from "@/lib/tenant/tenant"; 5 | import { addressOrEnsNameWrap } from "../utils/ensName"; 6 | 7 | const isCitizen = async (addressOrEnsName: string) => 8 | addressOrEnsNameWrap(isCitizenForAddress, addressOrEnsName); 9 | 10 | async function isCitizenForAddress({ address }: { address: string }) { 11 | const { slug } = Tenant.current(); 12 | 13 | const citizen = await prismaWeb2Client.$queryRaw< 14 | { 15 | address: string; 16 | }[] 17 | >( 18 | Prisma.sql` 19 | SELECT address 20 | FROM agora.citizens 21 | WHERE dao_slug = ${slug}::config.dao_slug 22 | AND retro_funding_round = (SELECT MAX(retro_funding_round) FROM agora.citizens) 23 | AND LOWER(address) = LOWER(${address}); 24 | ` 25 | ); 26 | 27 | return citizen.length > 0; 28 | } 29 | 30 | export const fetchIsCitizen = cache(isCitizen); 31 | -------------------------------------------------------------------------------- /src/app/api/common/comments/deleteImpactMetricComment.ts: -------------------------------------------------------------------------------- 1 | import { cache } from "react"; 2 | import { prismaWeb2Client } from "@/app/lib/prisma"; 3 | 4 | async function deleteImpactMetricCommentApi({ 5 | commentId, 6 | }: { 7 | commentId: number; 8 | }) { 9 | const deletedComment = await prismaWeb2Client.metrics_comments.delete({ 10 | where: { 11 | comment_id: commentId, 12 | }, 13 | include: { 14 | metrics_comments_votes: true, 15 | }, 16 | }); 17 | 18 | return { 19 | comment_id: deletedComment.comment_id, 20 | }; 21 | } 22 | 23 | export const deleteImpactMetricComment = cache(deleteImpactMetricCommentApi); 24 | -------------------------------------------------------------------------------- /src/app/api/common/comments/getImpactMetricCommentVotes.ts: -------------------------------------------------------------------------------- 1 | import { cache } from "react"; 2 | import { ImpactMetricCommentVote } from "./impactMetricComment"; 3 | import { prismaWeb2Client } from "@/app/lib/prisma"; 4 | 5 | async function getImpactMetricCommentVotesApi({ 6 | commentId, 7 | }: { 8 | commentId: number; 9 | }): Promise { 10 | const votes = await prismaWeb2Client.metrics_comments_votes.findMany({ 11 | where: { 12 | comment_id: commentId, 13 | }, 14 | }); 15 | 16 | return votes.map((vote) => { 17 | return { 18 | comment_id: vote.comment_id, 19 | address: vote.voter, 20 | vote: vote.vote, 21 | created_at: vote.created_at, 22 | updated_at: vote.updated_at, 23 | }; 24 | }); 25 | } 26 | 27 | export const fetchImpactMetricCommentVotes = cache( 28 | getImpactMetricCommentVotesApi 29 | ); 30 | -------------------------------------------------------------------------------- /src/app/api/common/comments/impactMetricComment.d.ts: -------------------------------------------------------------------------------- 1 | import { Prisma } from "@prisma/client"; 2 | 3 | export type ImpactMetrciCommentPayload = 4 | Prisma.metrics_commentsGetPayload & { 5 | metrics_comments_votes: Prisma.metrics_comments_votesGetPayload[]; 6 | }; 7 | 8 | export type ImpactMetricComment = { 9 | comment_id: number; 10 | comment: string; 11 | address: string; 12 | created_at: Date; 13 | updated_at: Date; 14 | votes_count: number; 15 | votes: ImpactMetricCommentVote[]; 16 | }; 17 | 18 | export type ImpactMetricCommentVote = { 19 | comment_id: number; 20 | address: string; 21 | vote: number; 22 | created_at: Date; 23 | updated_at: Date; 24 | }; 25 | -------------------------------------------------------------------------------- /src/app/api/common/delegateStatement/delegateStatement.d.ts: -------------------------------------------------------------------------------- 1 | import { DelegateStatements } from "@prisma/client"; 2 | 3 | export type DelegateStatement = Omit< 4 | DelegateStatements, 5 | "createdAt" | "updatedAt" | "signature" | "dao_slug" 6 | >; 7 | -------------------------------------------------------------------------------- /src/app/api/common/delegates/getDelegateForSCW.ts: -------------------------------------------------------------------------------- 1 | import "server-only"; 2 | 3 | import { prismaWeb2Client } from "@/app/lib/prisma"; 4 | import Tenant from "@/lib/tenant/tenant"; 5 | import { unstable_cache } from "next/cache"; 6 | 7 | // Returns an owner delegate for a given SCW address 8 | async function getDelegateForSCW(address: string) { 9 | const { slug } = Tenant.current(); 10 | 11 | return prismaWeb2Client.delegateStatements 12 | .findFirst({ 13 | where: { scw_address: address.toLowerCase(), dao_slug: slug }, 14 | }) 15 | .catch((error) => console.error(error)); 16 | } 17 | 18 | export const fetchDelegateForSCW = unstable_cache( 19 | async (address: string) => { 20 | return getDelegateForSCW(address); 21 | }, 22 | [], 23 | { 24 | revalidate: 60, // 1 minute cache 25 | } 26 | ); 27 | -------------------------------------------------------------------------------- /src/app/api/common/delegations/delegation.d.ts: -------------------------------------------------------------------------------- 1 | import { OptimismDelegatees } from "@prisma/client"; 2 | 3 | export type DelegateePayload = OptimismDelegatees; 4 | 5 | export type Delegation = { 6 | from: string; 7 | to: string; 8 | allowance: string; 9 | percentage: string; 10 | timestamp: Date | null; 11 | type: "DIRECT" | "ADVANCED"; 12 | amount: "FULL" | "PARTIAL"; 13 | transaction_hash: string; 14 | }; 15 | -------------------------------------------------------------------------------- /src/app/api/common/draftProposals/getDraftProposals.ts: -------------------------------------------------------------------------------- 1 | import { prismaWeb2Client } from "@/app/lib/prisma"; 2 | import { DraftProposal } from "@/app/proposals/draft/types"; 3 | import { cache } from "react"; 4 | 5 | const getDraftProposal = async (id: number) => { 6 | const draftProposal = await prismaWeb2Client.proposalDraft.findUnique({ 7 | where: { 8 | id: id, 9 | }, 10 | include: { 11 | transactions: true, 12 | social_options: true, 13 | checklist_items: true, 14 | approval_options: { 15 | include: { 16 | transactions: true, 17 | }, 18 | }, 19 | }, 20 | }); 21 | 22 | return draftProposal as DraftProposal; 23 | }; 24 | 25 | export const fetchDraftProposal = cache(getDraftProposal); 26 | -------------------------------------------------------------------------------- /src/app/api/common/impactMetrics/viewImactMetric.ts: -------------------------------------------------------------------------------- 1 | import { addressOrEnsNameWrap } from "../utils/ensName"; 2 | import { prismaWeb2Client } from "@/app/lib/prisma"; 3 | 4 | const viewImpactMetric = async ({ 5 | addressOrENSName, 6 | metricId, 7 | }: { 8 | addressOrENSName: string; 9 | metricId: string; 10 | }) => 11 | addressOrEnsNameWrap(viewImpactMetricForAddress, addressOrENSName, { 12 | metricId, 13 | }); 14 | 15 | async function viewImpactMetricForAddress({ 16 | address, 17 | metricId, 18 | }: { 19 | address: string; 20 | metricId: string; 21 | }) { 22 | return prismaWeb2Client.metrics_views.upsert({ 23 | where: { 24 | metric_id_address: { 25 | metric_id: metricId, 26 | address, 27 | }, 28 | }, 29 | update: {}, 30 | create: { 31 | metric_id: metricId, 32 | address, 33 | }, 34 | }); 35 | } 36 | 37 | export const viewImpactMetricApi = viewImpactMetric; 38 | -------------------------------------------------------------------------------- /src/app/api/common/metrics/metrics.d.ts: -------------------------------------------------------------------------------- 1 | export type Metrics = { 2 | votableSupply: string; 3 | totalSupply: string; 4 | quorum: string; 5 | }; 6 | -------------------------------------------------------------------------------- /src/app/api/common/metrics/route.ts: -------------------------------------------------------------------------------- 1 | // export const dynamic = 'force-dynamic'; // this line is uncommented for e2e tests 2 | 3 | import { NextResponse } from "next/server"; 4 | 5 | export async function GET() { 6 | const { fetchMetrics } = await import("./getMetrics"); 7 | 8 | try { 9 | const metrics = await fetchMetrics(); 10 | return NextResponse.json(metrics); 11 | } catch (e: any) { 12 | return new Response("Internal server error: " + e.toString(), { 13 | status: 500, 14 | }); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app/api/common/utils/bigIntRatio.ts: -------------------------------------------------------------------------------- 1 | export function calculateBigIntRatio( 2 | numerator: bigint, 3 | denominator: bigint 4 | ): string { 5 | if (!denominator || denominator <= 0n) return "0"; 6 | 7 | const magnitude = Math.abs( 8 | denominator.toString().length - numerator.toString().length + 3 9 | ); 10 | const scalingFactor = 10n ** BigInt(magnitude); 11 | 12 | const result = 13 | Number(numerator * scalingFactor) / Number(denominator * scalingFactor); 14 | 15 | const safeMaxDigits = Math.min(magnitude, 20); 16 | 17 | return result.toLocaleString("fullwide", { 18 | useGrouping: false, 19 | minimumFractionDigits: 0, 20 | maximumFractionDigits: safeMaxDigits, 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /src/app/api/common/utils/ensName.ts: -------------------------------------------------------------------------------- 1 | import { ensNameToAddress } from "@/app/lib/ENSUtils"; 2 | import { isAddress } from "viem"; 3 | 4 | export async function addressOrEnsNameWrap( 5 | handler: (args: T & { address: string }) => Promise

, 6 | addressOrENSName: string, 7 | args: T = {} as T 8 | ) { 9 | const address = isAddress(addressOrENSName) 10 | ? addressOrENSName.toLowerCase() 11 | : await ensNameToAddress(addressOrENSName); 12 | 13 | return await handler({ ...args, address }); 14 | } 15 | -------------------------------------------------------------------------------- /src/app/api/common/utils/frequencyHandling.ts: -------------------------------------------------------------------------------- 1 | export function frequencyToLookbackDayCount(frequency: string): { 2 | lookback: number; 3 | } { 4 | const periodLowerCase = frequency.toLowerCase(); 5 | 6 | let lookback: number; 7 | 8 | switch (periodLowerCase) { 9 | case "3d": 10 | lookback = 3; 11 | break; 12 | case "7d": 13 | lookback = 7; 14 | break; 15 | case "1m": 16 | case "30d": 17 | lookback = 30; 18 | break; 19 | case "3m": 20 | case "90d": 21 | lookback = 90; 22 | break; 23 | case "1y": 24 | lookback = 365; 25 | break; 26 | case "max": 27 | lookback = 365 * 10; 28 | break; 29 | default: 30 | throw new Error("Invalid frequency value"); 31 | } 32 | 33 | return { lookback }; 34 | } 35 | -------------------------------------------------------------------------------- /src/app/api/common/votableSupply/route.ts: -------------------------------------------------------------------------------- 1 | // export const dynamic = 'force-dynamic'; // this line is uncommented for e2e tests 2 | 3 | import { NextRequest, NextResponse } from "next/server"; 4 | 5 | export async function GET(request: NextRequest) { 6 | const { findVotableSupply } = await import("@/lib/prismaUtils"); 7 | const { default: Tenant } = await import("@/lib/tenant/tenant"); 8 | 9 | const { namespace, contracts } = Tenant.current(); 10 | const address = contracts.token.address; 11 | const slug = namespace; 12 | 13 | try { 14 | const response = await findVotableSupply({ namespace, address }); 15 | return NextResponse.json(response); 16 | } catch (e: any) { 17 | return new Response("Internal server error: " + e.toString(), { 18 | status: 500, 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/app/api/common/votes/route.ts: -------------------------------------------------------------------------------- 1 | import { type NextRequest, NextResponse } from "next/server"; 2 | 3 | export async function GET(request: NextRequest) { 4 | const { fetchAllForVoting } = await import("@/app/api/votes/getVotes"); 5 | 6 | const params = request.nextUrl.searchParams; 7 | const address = params.get("address"); 8 | const blockNumber = params.get("blockNumber"); 9 | const proposalId = params.get("proposalId"); 10 | 11 | if (!address || !blockNumber || !proposalId) { 12 | return new Response("Missing address, blockNumber, or proposalId", { 13 | status: 400, 14 | }); 15 | } 16 | 17 | try { 18 | const allVotes = await fetchAllForVoting( 19 | address, 20 | Number(blockNumber), 21 | proposalId 22 | ); 23 | return NextResponse.json(allVotes); 24 | } catch (e: any) { 25 | return new Response("Internal server error: " + e.toString(), { 26 | status: 500, 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/api/common/voting-power/votingPower.d.ts: -------------------------------------------------------------------------------- 1 | import { 2 | OptimismAdvancedVotingPower, 3 | OptimismVotingPower, 4 | OptimismVotingPowerSnaps, 5 | } from "@prisma/client"; 6 | 7 | export type AdvancedVotingPowerPayload = OptimismAdvancedVotingPower; 8 | export type VotingPowerPayload = OptimismVotingPower; 9 | 10 | export type VotingPowerSnapsPayload = OptimismVotingPowerSnaps; 11 | 12 | export type VotingPowerData = { 13 | directVP: string; 14 | advancedVP: string; 15 | totalVP: string; 16 | }; 17 | -------------------------------------------------------------------------------- /src/app/api/delegations/getDelegations.ts: -------------------------------------------------------------------------------- 1 | import { 2 | fetchCurrentDelegatees, 3 | fetchCurrentAdvancedDelegators, 4 | } from "../common/delegations/getDelegations"; 5 | import { 6 | fetchProxy, 7 | fetchVotingPowerAvailableForDirectDelegation, 8 | fetchVotingPowerAvailableForSubdelegation, 9 | fetchIsDelegatingToProxy, 10 | } from "@/app/api/common/voting-power/getVotingPower"; 11 | 12 | export const fetchAllForAdvancedDelegation = async (address: string) => { 13 | return await Promise.all([ 14 | fetchVotingPowerAvailableForSubdelegation(address), 15 | fetchIsDelegatingToProxy(address), 16 | fetchCurrentDelegatees(address), 17 | fetchProxy(address), 18 | fetchCurrentAdvancedDelegators(address), 19 | fetchVotingPowerAvailableForDirectDelegation(address), 20 | ]); 21 | }; 22 | -------------------------------------------------------------------------------- /src/app/api/images/og/assets/Inter-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/app/api/images/og/assets/Inter-Black.ttf -------------------------------------------------------------------------------- /src/app/api/images/og/assets/Inter-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/app/api/images/og/assets/Inter-Bold.ttf -------------------------------------------------------------------------------- /src/app/api/images/og/assets/Inter-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/app/api/images/og/assets/Inter-Medium.ttf -------------------------------------------------------------------------------- /src/app/api/images/og/assets/Inter-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/app/api/images/og/assets/Inter-Regular.ttf -------------------------------------------------------------------------------- /src/app/api/images/og/assets/Inter-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/app/api/images/og/assets/Inter-SemiBold.ttf -------------------------------------------------------------------------------- /src/app/api/images/og/assets/grid-share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/app/api/images/og/assets/grid-share.png -------------------------------------------------------------------------------- /src/app/api/images/og/assets/og-delegate-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/app/api/images/og/assets/og-delegate-bg.png -------------------------------------------------------------------------------- /src/app/api/images/og/assets/og-delegates-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/app/api/images/og/assets/og-delegates-bg.png -------------------------------------------------------------------------------- /src/app/api/images/og/assets/og-generic-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/app/api/images/og/assets/og-generic-bg.png -------------------------------------------------------------------------------- /src/app/api/images/og/assets/og-proposals-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/app/api/images/og/assets/og-proposals-bg.png -------------------------------------------------------------------------------- /src/app/api/paymaster/fetchPaymasterData.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import Tenant from "@/lib/tenant/tenant"; 4 | 5 | export const fetchPaymasterData = async (params: any) => { 6 | const { ui } = Tenant.current(); 7 | 8 | if (!ui.smartAccountConfig) { 9 | throw new Error("Missing Smart Account Config"); 10 | } 11 | 12 | const response = await fetch(ui.smartAccountConfig!.paymasterUrl, { 13 | method: "POST", 14 | headers: { 15 | "Content-Type": "application/json", 16 | }, 17 | body: JSON.stringify({ 18 | secret: process.env.PAYMASTER_SECRET, 19 | userOp: params, 20 | }), 21 | cache: "no-store", 22 | }); 23 | 24 | if (!response.ok) { 25 | throw new Error( 26 | "failed to fetch paymaster data:" + (await response.text()) 27 | ); 28 | } 29 | 30 | const { paymasterAndData }: { paymasterAndData: `0x${string}` } = 31 | await response.json(); 32 | 33 | return paymasterAndData; 34 | }; 35 | -------------------------------------------------------------------------------- /src/app/api/proposals/[proposalId]/chart/route.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest, NextResponse } from "next/server"; 2 | 3 | export async function GET( 4 | request: NextRequest, 5 | { params }: { params: { proposalId: string } } 6 | ) { 7 | const { getVotesChart } = await import("@/app/api/proposals/getVotesChart"); 8 | const { getSnapshotVotesChart } = await import( 9 | "@/app/api/proposals/getVotesChart" 10 | ); 11 | 12 | const searchParams = request.nextUrl.searchParams; 13 | const proposalType = searchParams.get("proposalType"); 14 | try { 15 | const votes = 16 | proposalType === "SNAPSHOT" 17 | ? await getSnapshotVotesChart({ 18 | proposalId: params.proposalId, 19 | }) 20 | : await getVotesChart({ 21 | proposalId: params.proposalId, 22 | }); 23 | return NextResponse.json(votes); 24 | } catch (e: any) { 25 | return new Response("Internal server error: " + e.toString(), { 26 | status: 500, 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/api/staking/getDeposit.ts: -------------------------------------------------------------------------------- 1 | import Tenant from "@/lib/tenant/tenant"; 2 | import { cache } from "react"; 3 | import { StakedDeposit } from "@/lib/types"; 4 | import { findStakedDeposit } from "@/lib/prismaUtils"; 5 | 6 | interface IFetchDepositProps { 7 | id: number; 8 | } 9 | 10 | export async function fetchDeposit({ 11 | id, 12 | }: IFetchDepositProps): Promise { 13 | const { namespace, token } = Tenant.current(); 14 | 15 | const deposit = await findStakedDeposit({ 16 | namespace, 17 | depositId: id, 18 | }); 19 | 20 | if (!deposit) { 21 | throw new Error(`Deposit with id ${id} not found`); 22 | } 23 | 24 | return { 25 | amount: Number(deposit.amount).toLocaleString().replace(/,/g, ""), 26 | delegatee: deposit.delegatee, 27 | depositor: deposit.depositor, 28 | id: Number(deposit.deposit_id), 29 | }; 30 | } 31 | 32 | export const apiFetchDeposit = cache(fetchDeposit); 33 | -------------------------------------------------------------------------------- /src/app/api/staking/getDeposits.ts: -------------------------------------------------------------------------------- 1 | import Tenant from "@/lib/tenant/tenant"; 2 | import { cache } from "react"; 3 | import { StakedDeposit } from "@/lib/types"; 4 | import { scientificNotationToPrecision } from "@/lib/utils"; 5 | import { findStakedDeposits } from "@/lib/prismaUtils"; 6 | 7 | export async function fetchStakedDepositsForAddress({ 8 | address, 9 | }: { 10 | address: string; 11 | }): Promise { 12 | const { namespace } = Tenant.current(); 13 | const deposits = await findStakedDeposits({ 14 | namespace, 15 | address, 16 | }); 17 | 18 | return deposits.map((deposit) => { 19 | return { 20 | amount: scientificNotationToPrecision( 21 | deposit.amount.toString() 22 | ).toString(), 23 | delegatee: deposit.delegatee, 24 | depositor: deposit.depositor, 25 | id: Number(deposit.deposit_id), 26 | }; 27 | }); 28 | } 29 | 30 | export const apiFetchStakedDeposits = cache(fetchStakedDepositsForAddress); 31 | -------------------------------------------------------------------------------- /src/app/api/v1/apiUtils.ts: -------------------------------------------------------------------------------- 1 | import * as log from "@/app/lib/logging"; 2 | import * as otel from "@opentelemetry/api"; 3 | import { SEMATTRS_ENDUSER_ID } from "@opentelemetry/semantic-conventions"; 4 | 5 | export const traceWithUserId = (userId: string, fn: () => Promise) => { 6 | log.addSpanAttributes({ [SEMATTRS_ENDUSER_ID]: userId }); 7 | // Next JS is instrumented with OTel, and as such, any API call will have 8 | // an active span and context. The below cast should be safe if used in an API route. 9 | const ctxt = log.addBaggage({ 10 | [SEMATTRS_ENDUSER_ID]: userId as string, 11 | }) as otel.Context; 12 | return otel.context.with(ctxt, fn); 13 | }; 14 | -------------------------------------------------------------------------------- /src/app/api/v1/auth/nonce/route.ts: -------------------------------------------------------------------------------- 1 | // export const dynamic = 'force-dynamic'; // this line is uncommented for e2e tests 2 | 3 | import { NextResponse } from "next/server"; 4 | 5 | export async function GET() { 6 | const { generateNonce } = await import("siwe"); 7 | 8 | try { 9 | const nonce = generateNonce(); 10 | return NextResponse.json({ nonce }); 11 | } catch (e: any) { 12 | return new Response("Internal server error: " + e.toString(), { 13 | status: 500, 14 | }); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app/api/v1/contracts/governor/route.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest, NextResponse } from "next/server"; 2 | import { traceWithUserId } from "../../apiUtils"; 3 | 4 | export async function GET(request: NextRequest) { 5 | const { authenticateApiUser } = await import("@/app/lib/auth/serverAuth"); 6 | const { default: Tenant } = await import("@/lib/tenant/tenant"); 7 | 8 | const authResponse = await authenticateApiUser(request); 9 | 10 | if (!authResponse.authenticated) { 11 | return new Response(authResponse.failReason, { status: 401 }); 12 | } 13 | 14 | return await traceWithUserId(authResponse.userId as string, async () => { 15 | try { 16 | const { contracts } = Tenant.current(); 17 | return NextResponse.json({ 18 | address: contracts.governor.address, 19 | abi: contracts.governor.abi, 20 | }); 21 | } catch (e: any) { 22 | return new Response("Internal server error: " + e.toString(), { 23 | status: 500, 24 | }); 25 | } 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /src/app/api/v1/relay/getRelayStatus.ts: -------------------------------------------------------------------------------- 1 | import { getPublicClient } from "@/lib/viem"; 2 | import { cache } from "react"; 3 | import { formatEther } from "viem"; 4 | import { privateKeyToAccount } from "viem/accounts"; 5 | 6 | const SPONSOR_PRIVATE_KEY = process.env.GAS_SPONSOR_PK; 7 | const GAS_COST = 0.001108297; 8 | 9 | async function getRelayStatus() { 10 | if (!SPONSOR_PRIVATE_KEY) { 11 | throw new Error("SPONSOR_PRIVATE_KEY is not set"); 12 | } 13 | const publicClient = getPublicClient(); 14 | const account = privateKeyToAccount(SPONSOR_PRIVATE_KEY as `0x${string}`); 15 | 16 | const balance = await publicClient.getBalance({ 17 | address: account.address, 18 | }); 19 | 20 | return { 21 | balance: Number(formatEther(balance)), 22 | remaining_votes: Math.floor(Number(formatEther(balance)) / GAS_COST), 23 | }; 24 | } 25 | 26 | export const apiFetchRelayStatus = cache(getRelayStatus); 27 | -------------------------------------------------------------------------------- /src/app/api/v1/relay/route.ts: -------------------------------------------------------------------------------- 1 | import { type NextRequest, NextResponse } from "next/server"; 2 | import { apiFetchRelayStatus } from "./getRelayStatus"; 3 | 4 | export async function GET() { 5 | try { 6 | const status = await apiFetchRelayStatus(); 7 | return NextResponse.json(status); 8 | } catch (e: any) { 9 | return new Response("Internal server error: " + e.toString(), { 10 | status: 500, 11 | }); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/app/api/v1/votable_supply/route.ts: -------------------------------------------------------------------------------- 1 | // export const dynamic = 'force-dynamic'; // this line is uncommented for e2e tests 2 | 3 | import { NextResponse, type NextRequest } from "next/server"; 4 | 5 | export async function GET(request: NextRequest) { 6 | const { fetchVotableSupply } = await import( 7 | "@/app/api/common/votableSupply/getVotableSupply" 8 | ); 9 | 10 | try { 11 | const votable_supply = await fetchVotableSupply(); 12 | return NextResponse.json({ votable_supply }); 13 | } catch (e: any) { 14 | return new Response("Internal server error: " + e.toString(), { 15 | status: 500, 16 | }); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/app/delegates/[addressOrENSName]/components/SCWRedirect.tsx: -------------------------------------------------------------------------------- 1 | import { fetchDelegateForSCW } from "@/app/api/common/delegates/getDelegateForSCW"; 2 | import { redirect } from "next/navigation"; 3 | import Tenant from "@/lib/tenant/tenant"; 4 | 5 | // Redirect from a smart contract wallet address to the owner delegate page 6 | export const SCWRedirect = async ({ address }: { address: string }) => { 7 | const { ui } = Tenant.current(); 8 | const scwConfig = ui.smartAccountConfig; 9 | 10 | if (!scwConfig) { 11 | return null; 12 | } 13 | 14 | const delegate = await fetchDelegateForSCW(address); 15 | if (delegate) { 16 | redirect(`/delegates/${delegate.address}`); 17 | } 18 | 19 | return null; 20 | }; 21 | -------------------------------------------------------------------------------- /src/app/delegates/[addressOrENSName]/error.tsx: -------------------------------------------------------------------------------- 1 | "use client"; // Error boundaries must be Client Components 2 | 3 | import ResourceNotFound from "@/components/shared/ResourceNotFound/ResourceNotFound"; 4 | 5 | export default function NotFound() { 6 | return ; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/delegates/[addressOrENSName]/loading.tsx: -------------------------------------------------------------------------------- 1 | import AgoraLoader, { 2 | LogoLoader, 3 | } from "@/components/shared/AgoraLoader/AgoraLoader"; 4 | import Tenant from "@/lib/tenant/tenant"; 5 | 6 | export default function Loading() { 7 | const { ui } = Tenant.current(); 8 | const shouldHideAgoraBranding = ui.hideAgoraBranding; 9 | 10 | if (shouldHideAgoraBranding) { 11 | return ; 12 | } 13 | 14 | return ; 15 | } 16 | -------------------------------------------------------------------------------- /src/app/delegates/[addressOrENSName]/not-found.tsx: -------------------------------------------------------------------------------- 1 | import ResourceNotFound from "@/components/shared/ResourceNotFound/ResourceNotFound"; 2 | 3 | export default function NotFound() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /src/app/delegates/[addressOrENSName]/params.ts: -------------------------------------------------------------------------------- 1 | import { parseAsString } from "nuqs/server"; 2 | 3 | // Define the tab parameter schema for server-side parsing 4 | export const profileSearchParams = { 5 | tab: parseAsString.withDefault("statement"), 6 | }; 7 | 8 | // Server-side loader function 9 | export const loadProfileSearchParams = ( 10 | searchParams: Record 11 | ) => { 12 | // Get the tab value from search params 13 | const tabValue = 14 | typeof searchParams.tab === "string" ? searchParams.tab : undefined; 15 | 16 | // Validate the tab value 17 | const validTabs = ["statement", "participation", "delegations"]; 18 | const tab = tabValue && validTabs.includes(tabValue) ? tabValue : "statement"; 19 | 20 | return { 21 | tab, 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /src/app/delegates/loading.tsx: -------------------------------------------------------------------------------- 1 | import AgoraLoader, { 2 | LogoLoader, 3 | } from "@/components/shared/AgoraLoader/AgoraLoader"; 4 | import Tenant from "@/lib/tenant/tenant"; 5 | 6 | export default function Loading() { 7 | const { ui } = Tenant.current(); 8 | const shouldHideAgoraBranding = ui.hideAgoraBranding; 9 | 10 | if (shouldHideAgoraBranding) { 11 | return ; 12 | } 13 | 14 | return ; 15 | } 16 | -------------------------------------------------------------------------------- /src/app/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/app/icon.png -------------------------------------------------------------------------------- /src/app/lib/auth/types.ts: -------------------------------------------------------------------------------- 1 | export type AuthInfo = { 2 | // change name of this field, as it's currently a misnomer; or at least tri-valued 3 | authenticated: boolean; 4 | userId?: string; 5 | scope?: string; 6 | failReason?: string; 7 | type?: "api_key" | "jwt"; 8 | token?: string; 9 | }; 10 | -------------------------------------------------------------------------------- /src/app/lib/dynamodb.js: -------------------------------------------------------------------------------- 1 | import { DynamoDB } from "@aws-sdk/client-dynamodb"; 2 | 3 | export function makeDynamoClient() { 4 | return new DynamoDB({ 5 | // Explicitly specify endpoint to avoid rule evaluation (which uses create Function). 6 | endpoint: "https://dynamodb.us-east-2.amazonaws.com", 7 | region: "us-east-2", 8 | apiVersion: "2012-08-10", 9 | credentials: { 10 | accessKeyId: process.env.AWS_ACCESS_KEY_ID, 11 | secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, 12 | }, 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /src/app/lib/middleware/authenticateAgoraApiUser.js: -------------------------------------------------------------------------------- 1 | import { NextResponse } from "next/server"; 2 | import { headers } from "next/headers"; 3 | 4 | export function authenticateAgoraApiUser(request) { 5 | const headersList = headers(); 6 | const apiKey = headersList.get("agora-api-key"); 7 | 8 | // Use your method of checking the API user's key 9 | const apiUser = true; 10 | 11 | if (!apiUser) { 12 | return NextResponse.redirect(new URL("/api/forbidden", request.url)); 13 | } 14 | 15 | return null; 16 | } 17 | -------------------------------------------------------------------------------- /src/app/lib/utils/color.ts: -------------------------------------------------------------------------------- 1 | export function rgbStringToHex(rgbString: string | undefined): string { 2 | if (!rgbString) return "#000000"; 3 | 4 | const [r, g, b] = rgbString.split(" ").map(Number); 5 | return ( 6 | "#" + 7 | [r, g, b] 8 | .map((x) => { 9 | const hex = x.toString(16); 10 | return hex.length === 1 ? "0" + hex : hex; 11 | }) 12 | .join("") 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /src/app/lib/utils/og.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs/promises"; 2 | import path from "path"; 3 | 4 | export async function loadFont(filename: string): Promise { 5 | const filePath = path.join(process.cwd(), "public", "fonts", filename); 6 | return fs.readFile(filePath); 7 | } 8 | 9 | export async function loadImage(filename: string): Promise { 10 | const filePath = path.join(process.cwd(), "public", "images", filename); 11 | const imageBuffer = await fs.readFile(filePath); 12 | const base64Image = Buffer.from(imageBuffer).toString("base64"); 13 | const mimeType = "image/png"; // Adjust if your image is not a PNG 14 | return `data:${mimeType};base64,${base64Image}`; 15 | } 16 | -------------------------------------------------------------------------------- /src/app/lib/utils/text.ts: -------------------------------------------------------------------------------- 1 | export const cleanString = (text: string) => { 2 | return text 3 | .replace(/#{1,6}\s/g, "") // Removes Markdown headings 4 | .replace(/\n/g, " "); // Replaces newlines with space 5 | }; 6 | 7 | export const truncateString = (str: string, maxLength: number) => { 8 | return str.length > maxLength ? `${str.substring(0, maxLength - 3)}...` : str; 9 | }; 10 | 11 | export const truncateAddress = (address: string) => { 12 | return ( 13 | address && 14 | `${address.substring(0, 4)}...${address.substring(address?.length - 4)}` 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /src/app/loading.tsx: -------------------------------------------------------------------------------- 1 | import AgoraLoader, { 2 | LogoLoader, 3 | } from "@/components/shared/AgoraLoader/AgoraLoader"; 4 | import Tenant from "@/lib/tenant/tenant"; 5 | 6 | export default function Loading() { 7 | const { ui } = Tenant.current(); 8 | const shouldHideAgoraBranding = ui.hideAgoraBranding; 9 | 10 | if (shouldHideAgoraBranding) { 11 | return ; 12 | } 13 | 14 | return ; 15 | } 16 | -------------------------------------------------------------------------------- /src/app/not-found.tsx: -------------------------------------------------------------------------------- 1 | // export const dynamic = 'force-dynamic'; // this line is uncommented for e2e tests 2 | 3 | import ResourceNotFound from "@/components/shared/ResourceNotFound/ResourceNotFound"; 4 | 5 | export default function NotFound() { 6 | return ; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/proposals/draft/[id]/loading.tsx: -------------------------------------------------------------------------------- 1 | export default function Loading() { 2 | return ( 3 |

4 |
5 |

Loading...

6 |
7 |
8 |
9 |
10 |
11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /src/app/proposals/draft/actions/deleteDraftProposal.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { prismaWeb2Client } from "@/app/lib/prisma"; 4 | 5 | export type FormState = { 6 | ok: boolean; 7 | message: string; 8 | }; 9 | 10 | // TODO: need to auth this route in some way 11 | // perhaps send down the owner address and a signature + nonce to verify 12 | export async function onSubmitAction( 13 | draftProposalId: number 14 | ): Promise { 15 | try { 16 | // TODO: maybe we don't delete, we just flag isDeleted 17 | await prismaWeb2Client.proposalDraft.delete({ 18 | where: { 19 | id: draftProposalId, 20 | }, 21 | }); 22 | 23 | return { 24 | ok: true, 25 | message: "Success!", 26 | }; 27 | } catch (error) { 28 | console.log(error); 29 | return { 30 | ok: false, 31 | message: "Error deleting draft proposal", 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/app/proposals/draft/actions/revalidatePath.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { revalidatePath } from "next/cache"; 4 | 5 | export type FormState = { 6 | ok: boolean; 7 | message: string; 8 | }; 9 | 10 | export const invalidatePath = (id: number): FormState => { 11 | revalidatePath(`/proposals/draft/${id}`); 12 | 13 | return { 14 | ok: true, 15 | message: "Success!", 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /src/app/proposals/draft/components/AvatarAdress.tsx: -------------------------------------------------------------------------------- 1 | import { useEnsName, useEnsAvatar } from "wagmi"; 2 | import { truncateAddress } from "@/app/lib/utils/text"; 3 | 4 | const AvatarAddress = ({ address }: { address: `0x${string}` }) => { 5 | const { data: ensName } = useEnsName({ 6 | chainId: 1, 7 | address: address, 8 | }); 9 | 10 | const { data: ensAvatar } = useEnsAvatar({ 11 | chainId: 1, 12 | name: ensName as string, 13 | }); 14 | return ( 15 | 16 | {ensAvatar && } 17 |

{ensName ? ensName : truncateAddress(address)}

18 |
19 | ); 20 | }; 21 | 22 | export default AvatarAddress; 23 | -------------------------------------------------------------------------------- /src/app/proposals/draft/components/BackButton.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Link from "next/link"; 4 | import { ChevronLeftIcon } from "@heroicons/react/20/solid"; 5 | 6 | const BackButton = ({ 7 | draftProposalId, 8 | index, 9 | }: { 10 | draftProposalId: number; 11 | index: number; 12 | }) => { 13 | return ( 14 | 18 | 19 | 20 | ); 21 | }; 22 | 23 | export default BackButton; 24 | -------------------------------------------------------------------------------- /src/app/proposals/draft/components/OptimisticProposalForm.tsx: -------------------------------------------------------------------------------- 1 | // Optimistic proposals have no form. 2 | // Creating a component for readability so the component that renders 3 | // this component is not returning the null. This feels more clear 4 | // -MG 5 | const OptimisticProposalForm = () => { 6 | return null; 7 | }; 8 | 9 | export default OptimisticProposalForm; 10 | -------------------------------------------------------------------------------- /src/app/proposals/draft/schemas/requestSponsorshipSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { isAddress } from "viem"; 3 | 4 | export const schema = z.object({ 5 | sponsor_address: z 6 | .string() 7 | .trim() 8 | .refine((value) => isAddress(value), { 9 | message: "Invalid address.", 10 | }), 11 | }); 12 | -------------------------------------------------------------------------------- /src/app/proposals/draft/schemas/sponsorProposalSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const schema = z.object({ 4 | snapshot_link: z 5 | .string() 6 | .min(1, { message: "Snapshot link cannot be empty" }) 7 | .optional(), 8 | onchain_transaction_hash: z 9 | .string() 10 | .min(1, { message: "TxHash cannot be empty" }) 11 | .optional(), 12 | }); 13 | -------------------------------------------------------------------------------- /src/app/proposals/draft/schemas/tempCheckSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { isURL } from "@/lib/utils"; 3 | 4 | export const schema = z.object({ 5 | temp_check_link: z 6 | .string() 7 | .trim() 8 | .refine((value) => isURL(value), { 9 | message: "Invalid URL.", 10 | }) 11 | .optional(), 12 | }); 13 | -------------------------------------------------------------------------------- /src/app/proposals/loading.tsx: -------------------------------------------------------------------------------- 1 | import AgoraLoader, { 2 | LogoLoader, 3 | } from "@/components/shared/AgoraLoader/AgoraLoader"; 4 | import Tenant from "@/lib/tenant/tenant"; 5 | 6 | export default function Loading() { 7 | const { ui } = Tenant.current(); 8 | const shouldHideAgoraBranding = ui.hideAgoraBranding; 9 | 10 | if (shouldHideAgoraBranding) { 11 | return ; 12 | } 13 | 14 | return ; 15 | } 16 | -------------------------------------------------------------------------------- /src/app/proposals/page.tsx: -------------------------------------------------------------------------------- 1 | // export const dynamic = 'force-dynamic'; // this line is uncommented for e2e tests 2 | 3 | import ProposalsHome from "@/components/Proposals/ProposalsHome"; 4 | 5 | export const revalidate = 60; 6 | 7 | export { generateMetadata } from "../page"; 8 | export default ProposalsHome; 9 | -------------------------------------------------------------------------------- /src/app/proposals/sponsor/components/SponsorForm.tsx: -------------------------------------------------------------------------------- 1 | import DraftPreview from "../../draft/components/DraftPreview"; 2 | import SponsorActions from "./SponsorActions"; 3 | import { DraftProposal } from "../../../proposals/draft/types"; 4 | 5 | const SponsorForm = ({ draftProposal }: { draftProposal: DraftProposal }) => { 6 | return ( 7 | } 10 | /> 11 | ); 12 | }; 13 | 14 | export default SponsorForm; 15 | -------------------------------------------------------------------------------- /src/app/staking/components/RedirectOrConnect.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useAccount } from "wagmi"; 4 | import { useEffect } from "react"; 5 | import { redirect } from "next/navigation"; 6 | import { Button } from "@/components/ui/button"; 7 | import { useModal } from "connectkit"; 8 | 9 | export const RedirectOrConnect = () => { 10 | const { setOpen } = useModal(); 11 | const { address, isConnected } = useAccount(); 12 | 13 | useEffect(() => { 14 | if (address && isConnected) { 15 | redirect(`/staking/${address}`); 16 | } 17 | }, [address, isConnected]); 18 | 19 | return ( 20 | 26 | ); 27 | }; 28 | -------------------------------------------------------------------------------- /src/app/staking/components/StakingIntro.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export const StakingIntro = () => { 4 | return ( 5 | <> 6 |
7 | Introducing staking, the next chapter of Uniswap Governance 8 |
9 |
10 | UNI holders are now eligible to collect a small fee on trades executed 11 | on some Uniswap protocol liquidity pools. To collect these fees, UNI 12 | holders must stake their tokens. 13 |
14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /src/app/staking/components/receipt/ReceiptContainer.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from "react"; 2 | 3 | interface IReceiptContainerProps { 4 | children: ReactNode; 5 | } 6 | 7 | const ReceiptContainer = ({ children }: IReceiptContainerProps) => { 8 | return ( 9 |
10 | {children} 11 |
12 | ); 13 | }; 14 | 15 | export default ReceiptContainer; 16 | -------------------------------------------------------------------------------- /src/assets/icons/arrow_right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/icons/ballot.svg: -------------------------------------------------------------------------------- 1 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /src/assets/icons/calendar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/cardView.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/icons/checkCircleBroken.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/coins.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/currency.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/icons/delegateAvatar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/assets/icons/expand.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/file.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/flag.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/lightbulb.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/liquid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/logout.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/icons/northEast.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/power.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/spinner.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/tableView.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/icons/users.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/vote.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/assets/icons/wallet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/walletConnected.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/optimism/agora_optimism_og_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/optimism/agora_optimism_og_image.png -------------------------------------------------------------------------------- /src/assets/optimism/optimism_hero_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/optimism/optimism_hero_background.png -------------------------------------------------------------------------------- /src/assets/optimism/spark2-sticker.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/tenant/b3_info_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/b3_info_1.png -------------------------------------------------------------------------------- /src/assets/tenant/b3_info_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/b3_info_2.png -------------------------------------------------------------------------------- /src/assets/tenant/b3_info_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/b3_info_3.png -------------------------------------------------------------------------------- /src/assets/tenant/b3_info_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/b3_info_4.png -------------------------------------------------------------------------------- /src/assets/tenant/b3_info_hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/b3_info_hero.png -------------------------------------------------------------------------------- /src/assets/tenant/boost_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/boost_banner.png -------------------------------------------------------------------------------- /src/assets/tenant/boost_hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/boost_hero.png -------------------------------------------------------------------------------- /src/assets/tenant/boost_info_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/boost_info_1.png -------------------------------------------------------------------------------- /src/assets/tenant/boost_info_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/boost_info_2.png -------------------------------------------------------------------------------- /src/assets/tenant/boost_info_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/boost_info_3.png -------------------------------------------------------------------------------- /src/assets/tenant/boost_info_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/boost_info_4.png -------------------------------------------------------------------------------- /src/assets/tenant/cyber_info_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/cyber_info_1.png -------------------------------------------------------------------------------- /src/assets/tenant/cyber_info_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/cyber_info_2.png -------------------------------------------------------------------------------- /src/assets/tenant/cyber_info_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/cyber_info_3.png -------------------------------------------------------------------------------- /src/assets/tenant/cyber_info_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/cyber_info_4.png -------------------------------------------------------------------------------- /src/assets/tenant/cyber_info_hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/cyber_info_hero.png -------------------------------------------------------------------------------- /src/assets/tenant/demo_delegate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/assets/tenant/demo_discord.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/demo_discord.png -------------------------------------------------------------------------------- /src/assets/tenant/demo_docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/demo_docs.png -------------------------------------------------------------------------------- /src/assets/tenant/demo_forum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/demo_forum.png -------------------------------------------------------------------------------- /src/assets/tenant/demo_hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/demo_hero.png -------------------------------------------------------------------------------- /src/assets/tenant/demo_vision.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/demo_vision.png -------------------------------------------------------------------------------- /src/assets/tenant/derive_delegate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/tenant/derive_gov.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/derive_gov.png -------------------------------------------------------------------------------- /src/assets/tenant/derive_info_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/derive_info_0.png -------------------------------------------------------------------------------- /src/assets/tenant/derive_info_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/derive_info_1.png -------------------------------------------------------------------------------- /src/assets/tenant/derive_info_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/derive_info_2.png -------------------------------------------------------------------------------- /src/assets/tenant/derive_info_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/derive_info_3.png -------------------------------------------------------------------------------- /src/assets/tenant/ens_info_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/ens_info_0.png -------------------------------------------------------------------------------- /src/assets/tenant/ens_info_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/ens_info_1.png -------------------------------------------------------------------------------- /src/assets/tenant/ens_info_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/ens_info_2.png -------------------------------------------------------------------------------- /src/assets/tenant/ens_info_hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/ens_info_hero.png -------------------------------------------------------------------------------- /src/assets/tenant/linea_info_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/linea_info_1.png -------------------------------------------------------------------------------- /src/assets/tenant/linea_info_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/linea_info_2.png -------------------------------------------------------------------------------- /src/assets/tenant/linea_info_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/linea_info_3.png -------------------------------------------------------------------------------- /src/assets/tenant/linea_info_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/linea_info_4.png -------------------------------------------------------------------------------- /src/assets/tenant/linea_info_hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/linea_info_hero.png -------------------------------------------------------------------------------- /src/assets/tenant/optimism_info_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/optimism_info_1.png -------------------------------------------------------------------------------- /src/assets/tenant/optimism_info_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/optimism_info_2.png -------------------------------------------------------------------------------- /src/assets/tenant/optimism_info_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/optimism_info_3.png -------------------------------------------------------------------------------- /src/assets/tenant/optimism_info_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/optimism_info_4.png -------------------------------------------------------------------------------- /src/assets/tenant/optimism_info_hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/optimism_info_hero.png -------------------------------------------------------------------------------- /src/assets/tenant/scroll_delegate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/assets/tenant/scroll_favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/scroll_favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /src/assets/tenant/scroll_favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/scroll_favicon/favicon-16x16.png -------------------------------------------------------------------------------- /src/assets/tenant/scroll_favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/scroll_favicon/favicon-32x32.png -------------------------------------------------------------------------------- /src/assets/tenant/scroll_favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/scroll_favicon/favicon.ico -------------------------------------------------------------------------------- /src/assets/tenant/scroll_gov.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/scroll_gov.png -------------------------------------------------------------------------------- /src/assets/tenant/scroll_hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/scroll_hero.png -------------------------------------------------------------------------------- /src/assets/tenant/scroll_info_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/scroll_info_1.png -------------------------------------------------------------------------------- /src/assets/tenant/scroll_info_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/scroll_info_2.png -------------------------------------------------------------------------------- /src/assets/tenant/scroll_info_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/scroll_info_3.png -------------------------------------------------------------------------------- /src/assets/tenant/scroll_info_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/scroll_info_4.png -------------------------------------------------------------------------------- /src/assets/tenant/scroll_info_hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/scroll_info_hero.png -------------------------------------------------------------------------------- /src/assets/tenant/uniswap_info_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/uniswap_info_1.png -------------------------------------------------------------------------------- /src/assets/tenant/uniswap_info_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/uniswap_info_2.png -------------------------------------------------------------------------------- /src/assets/tenant/uniswap_info_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/uniswap_info_3.png -------------------------------------------------------------------------------- /src/assets/tenant/uniswap_info_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/uniswap_info_4.png -------------------------------------------------------------------------------- /src/assets/tenant/uniswap_info_hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voteagora/agora-next/74369fa28c8abe969b893c8defab4191b297fb17/src/assets/tenant/uniswap_info_hero.png -------------------------------------------------------------------------------- /src/components/Admin/AdminForm.tsx: -------------------------------------------------------------------------------- 1 | import ProposalTypeSettings from "./ProposalTypeSettings"; 2 | import GovernorSettings from "./GovernorSettings"; 3 | import FAQs from "./FAQs"; 4 | import { FormattedProposalType } from "@/lib/types"; 5 | import AdminAccountActions from "@/components/Admin/AdminAccountActions"; 6 | 7 | // TODO: Take init values from the chain 8 | export default function AdminForm({ 9 | votableSupply, 10 | proposalTypes, 11 | }: { 12 | votableSupply: string; 13 | proposalTypes: FormattedProposalType[]; 14 | }) { 15 | return ( 16 |
17 |
18 | 19 | 23 | 24 |
25 | 26 |
27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /src/components/Button.jsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | 3 | export function Button({ href = "", className = "", ...props }) { 4 | return ( 5 | <> 6 | {href ? ( 7 |
13 | {" "} 14 |
15 | ) : ( 16 | 31 | 32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /src/components/shared/HumanVote.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const HumanVote = ({ support }) => { 4 | let color; 5 | switch (support) { 6 | case "FOR": 7 | color = "green"; 8 | break; 9 | case "AGAINST": 10 | color = "red"; 11 | break; 12 | case "ABSTAIN": 13 | color = "black"; 14 | break; 15 | default: 16 | color = "black"; 17 | } 18 | return {support.toLowerCase()}; 19 | }; 20 | 21 | export default HumanVote; 22 | -------------------------------------------------------------------------------- /src/components/shared/InfoPop.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { QuestionMarkCircleIcon } from "@heroicons/react/20/solid"; 4 | import { useState } from "react"; 5 | 6 | export default function InfoPop({ children }: { children: React.ReactNode }) { 7 | const [isOpen, setIsOpen] = useState(false); 8 | return ( 9 |
setIsOpen(true)} 12 | onMouseLeave={() => setIsOpen(false)} 13 | > 14 | 15 | {isOpen && ( 16 |
17 | {children} 18 |
19 | )} 20 |
21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /src/components/shared/InputBox.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | export default function InputBox({ 4 | placeholder, 5 | onChange, 6 | value, 7 | ...props 8 | }: { 9 | placeholder: string; 10 | value: any; 11 | onChange: (next: any) => void; 12 | [key: string]: any; 13 | }) { 14 | return ( 15 | onChange(event.target.value)} 20 | onWheel={(e) => e.currentTarget.blur()} 21 | {...props} 22 | /> 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /src/components/shared/MultiButtons.tsx: -------------------------------------------------------------------------------- 1 | import { HStack } from "@/components/Layout/Stack"; 2 | import { Button } from "../ui/button"; 3 | 4 | export function MultiButtons({ 5 | buttonsProps, 6 | }: { 7 | buttonsProps: [string, () => void][]; 8 | }) { 9 | return ( 10 | 11 | {buttonsProps.map((buttonProps, index) => { 12 | return ( 13 | 22 | ); 23 | })} 24 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/components/shared/ResourceNotFound/ResourceNotFound.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Image from "next/image"; 4 | import logo from "@/assets/agora_logo.svg"; 5 | 6 | interface ResourceNotFoundProps { 7 | message?: string; 8 | } 9 | 10 | export default function ResourceNotFound({ message }: ResourceNotFoundProps) { 11 | const defaultMessage = "Hmm. Can't find what you're looking for."; 12 | return ( 13 |
14 | loading 21 |

{message || defaultMessage}

22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /src/components/shared/RouteNotSupported.tsx: -------------------------------------------------------------------------------- 1 | export const RouteNotSupported = () => { 2 | return
Route not supported.
; 3 | }; 4 | -------------------------------------------------------------------------------- /src/components/shared/TruncatedAddress.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const TruncatedAddress = ({ address }) => { 4 | const humanAddress = `${address.slice(0, 4)}...${address.slice(-4)}`; 5 | return {humanAddress}; 6 | }; 7 | 8 | export default TruncatedAddress; 9 | -------------------------------------------------------------------------------- /src/components/ui/separator.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as SeparatorPrimitive from "@radix-ui/react-separator"; 5 | 6 | import { cn } from "@/lib/utils"; 7 | 8 | const Separator = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >( 12 | ( 13 | { className, orientation = "horizontal", decorative = true, ...props }, 14 | ref 15 | ) => ( 16 | 27 | ) 28 | ); 29 | Separator.displayName = SeparatorPrimitive.Root.displayName; 30 | 31 | export { Separator }; 32 | -------------------------------------------------------------------------------- /src/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "@/lib/utils"; 4 | 5 | export interface TextareaProps 6 | extends React.TextareaHTMLAttributes {} 7 | 8 | const Textarea = React.forwardRef( 9 | ({ className, ...props }, ref) => { 10 | return ( 11 |