Skip to content

Commit

Permalink
refactor to get data from static rounds if subgraph is not available
Browse files Browse the repository at this point in the history
  • Loading branch information
yuetloo committed Jul 10, 2024
1 parent 547561d commit b17fed6
Show file tree
Hide file tree
Showing 22 changed files with 301 additions and 424 deletions.
127 changes: 120 additions & 7 deletions vue-app/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,20 @@
<div v-if="isSidebarShown" id="sidebar" :class="`${showCartPanel ? 'desktop-l' : 'desktop'}`">
<round-information />
</div>
<active-app v-if="isActiveApp" :is-sidebar-shown="isSidebarShown" :show-bread-crumb="showBreadCrumb" />
<static-app v-else :is-sidebar-shown="isSidebarShown" :show-bread-crumb="showBreadCrumb" />
<div
id="content"
:class="{
padded: isVerifyStep || (isSidebarShown && !isCartPadding),
'mr-cart-open': showCartPanel && isSideCartShown,
'mr-cart-closed': !showCartPanel && isSideCartShown,
}"
>
<breadcrumbs v-if="showBreadCrumb" />
<router-view :key="route.path" />
</div>
<div v-if="isSideCartShown" id="cart" :class="`desktop ${showCartPanel ? 'open-cart' : 'closed-cart'}`">
<cart-widget />
</div>
</div>
<mobile-tabs v-if="isMobileTabsShown" />
</div>
Expand All @@ -20,22 +32,29 @@
<script setup lang="ts">
import NavBar from '@/components/NavBar.vue'
import MobileTabs from '@/components/MobileTabs.vue'
import ActiveApp from './components/ActiveApp.vue'
import StaticApp from './components/StaticApp.vue'
// @ts-ignore
import { ModalsContainer } from 'vue-final-modal'
import { getDefaultColorScheme } from '@/utils/theme'
import { operator, isActiveApp } from '@/api/core'
import { useAppStore } from '@/stores'
import { operator } from '@/api/core'
import { useAppStore, useRecipientStore, useUserStore, useWalletStore } from '@/stores'
import { storeToRefs } from 'pinia'
import { useRoute } from 'vue-router'
import { useMeta } from 'vue-meta'
import { getCurrentRound } from './api/round'
import type { WalletUser } from '@/stores'
import type { BrowserProvider } from 'ethers'
const route = useRoute()
const appStore = useAppStore()
const { theme, showCartPanel } = storeToRefs(appStore)
const { theme, showCartPanel, currentRound } = storeToRefs(appStore)
const userStore = useUserStore()
const { currentUser } = storeToRefs(userStore)
const recipientStore = useRecipientStore()
const wallet = useWalletStore()
const { user: walletUser } = storeToRefs(wallet)
const appReady = ref(false)
// https://stackoverflow.com/questions/71785473/how-to-use-vue-meta-with-vue3
// https://www.npmjs.com/package/vue-meta/v/3.0.0-alpha.7
Expand Down Expand Up @@ -100,6 +119,100 @@ watch(theme, () => {
const savedTheme = theme.value
document.documentElement.setAttribute('data-theme', savedTheme || getDefaultColorScheme())
})
const intervals: { [key: string]: any } = {}
const isUserAndRoundLoaded = computed(() => !!currentUser.value && !!currentRound.value)
const isSideCartShown = computed(() => isUserAndRoundLoaded.value && isSidebarShown.value && routeName.value !== 'cart')
const isVerifyStep = computed(() => routeName.value === 'verify-step')
const isCartPadding = computed(() => {
const routes = ['cart']
return routes.includes(routeName.value)
})
function setupLoadIntervals() {
intervals.round = setInterval(() => {
appStore.loadRoundInfo()
}, 60 * 1000)
intervals.recipient = setInterval(async () => {
recipientStore.loadRecipientRegistryInfo()
}, 60 * 1000)
intervals.user = setInterval(() => {
userStore.loadUserInfo()
}, 60 * 1000)
}
onMounted(async () => {
try {
await wallet.reconnect()
} catch (err) {
/* eslint-disable-next-line no-console */
console.warn('Unable to reconnect wallet', err)
}
try {
const roundAddress = appStore.currentRoundAddress || (await getCurrentRound())
if (roundAddress) {
appStore.selectRound(roundAddress)
/* eslint-disable-next-line no-console */
console.log('roundAddress', roundAddress)
}
} catch (err) {
/* eslint-disable-next-line no-console */
console.warn('Failed to get current round:', err)
}
appReady.value = true
try {
await appStore.loadClrFundInfo()
await appStore.loadMACIFactoryInfo()
await appStore.loadRoundInfo()
await recipientStore.loadRecipientRegistryInfo()
appStore.isAppReady = true
setupLoadIntervals()
} catch (err) {
/* eslint-disable-next-line no-console */
console.warn('Failed to load application data:', err)
}
})
onBeforeUnmount(() => {
for (const interval of Object.keys(intervals)) {
clearInterval(intervals[interval])
}
})
watch(walletUser, async () => {
try {
if (walletUser.value) {
const user: WalletUser = {
chainId: walletUser.value.chainId,
walletAddress: walletUser.value.walletAddress,
web3Provider: walletUser.value.web3Provider as BrowserProvider,
}
// make sure factory is loaded
await appStore.loadClrFundInfo()
userStore.loginUser(user)
await userStore.loadUserInfo()
await userStore.loadBrightID()
} else {
await userStore.logoutUser()
}
} catch (err) {
/* eslint-disable-next-line no-console */
console.log('error', err)
}
})
watch(isUserAndRoundLoaded, async () => {
if (!isUserAndRoundLoaded.value) {
return
}
// load contribution when we get round information
await userStore.loadUserInfo()
})
</script>

<style lang="scss">
Expand Down
34 changes: 7 additions & 27 deletions vue-app/src/api/clrFund.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { clrfundContractAddress, clrFundContract, isActiveApp, provider } from './core'
import { clrfundContractAddress, clrFundContract, provider } from './core'
import sdk from '@/graphql/sdk'
import { ERC20 } from './abi'
import { Contract } from 'ethers'
Expand All @@ -20,32 +20,12 @@ export async function getClrFundInfo() {
let userRegistryAddress = ''
let recipientRegistryAddress = ''

try {
if (isActiveApp) {
const data = await sdk.GetClrFundInfo({
clrFundAddress: clrfundContractAddress.toLowerCase(),
})
const nativeTokenInfo = data.clrFund?.nativeTokenInfo
if (nativeTokenInfo) {
nativeTokenAddress = nativeTokenInfo.tokenAddress || ''
nativeTokenSymbol = nativeTokenInfo.symbol || ''
nativeTokenDecimals = Number(nativeTokenInfo.decimals) || 0
}

userRegistryAddress = data.clrFund?.contributorRegistryAddress || ''
recipientRegistryAddress = data.clrFund?.recipientRegistryAddress || ''
} else {
nativeTokenAddress = await clrFundContract.nativeToken()
const nativeTokenContract = new Contract(nativeTokenAddress, ERC20, provider)
nativeTokenSymbol = await nativeTokenContract.symbol()
nativeTokenDecimals = await nativeTokenContract.decimals()
userRegistryAddress = await clrFundContract.userRegistry()
recipientRegistryAddress = await clrFundContract.recipientRegistry()
}
} catch (err) {
/* eslint-disable-next-line no-console */
console.error('Failed GetClrFundInfo', err)
}
nativeTokenAddress = await clrFundContract.nativeToken().catch(() => '')
const nativeTokenContract = new Contract(nativeTokenAddress, ERC20, provider)
nativeTokenSymbol = await nativeTokenContract.symbol().catch(() => '')
nativeTokenDecimals = await nativeTokenContract.decimals().catch(() => 0)
userRegistryAddress = await clrFundContract.userRegistry().catch(() => '')
recipientRegistryAddress = await clrFundContract.recipientRegistry().catch(() => '')

try {
matchingPool = await getMatchingFunds(nativeTokenAddress)
Expand Down
41 changes: 29 additions & 12 deletions vue-app/src/api/contributions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { clrFundContract, provider } from './core'
import type { Project } from './projects'
import sdk from '@/graphql/sdk'
import { Transaction } from '@/utils/transaction'
import type { GetContributorIndexQuery, GetContributorMessagesQuery, GetTotalContributedQuery } from '@/graphql/API'

export const DEFAULT_CONTRIBUTION_AMOUNT = 5
export const MAX_CONTRIBUTION_AMOUNT = 10000 // See FundingRound.sol
Expand Down Expand Up @@ -99,9 +100,14 @@ export async function getTotalContributed(fundingRoundAddress: string): Promise<
const nativeToken = new Contract(nativeTokenAddress, ERC20, provider)
const balance = await nativeToken.balanceOf(fundingRoundAddress)

const data = await sdk.GetTotalContributed({
fundingRoundAddress: fundingRoundAddress.toLowerCase(),
})
let data: GetTotalContributedQuery
try {
data = await sdk.GetTotalContributed({
fundingRoundAddress: fundingRoundAddress.toLowerCase(),
})
} catch {
return { count: 0, amount: 0n }
}

if (!data.fundingRound?.contributorCount) {
return { count: 0, amount: 0n }
Expand Down Expand Up @@ -149,10 +155,16 @@ export async function getContributorIndex(maciAddress: string, pubKey: PubKey):
if (!maciAddress) {
return null
}
const id = getPubKeyId(maciAddress, pubKey)
const data = await sdk.GetContributorIndex({
publicKeyId: id,
})

let data: GetContributorIndexQuery
try {
const id = getPubKeyId(maciAddress, pubKey)
data = await sdk.GetContributorIndex({
publicKeyId: id,
})
} catch {
return null
}

if (data.publicKeys.length === 0) {
return null
Expand Down Expand Up @@ -197,11 +209,16 @@ export async function getContributorMessages({
return []
}

const key = getPubKeyId(maciAddress, contributorKey.pubKey)
const result = await sdk.GetContributorMessages({
pubKey: key,
contributorAddress: contributorAddress.toLowerCase(),
})
let result: GetContributorMessagesQuery
try {
const key = getPubKeyId(maciAddress, contributorKey.pubKey)
result = await sdk.GetContributorMessages({
pubKey: key,
contributorAddress: contributorAddress.toLowerCase(),
})
} catch {
return []
}

if (!(result.messages && result.messages.length)) {
return []
Expand Down
5 changes: 1 addition & 4 deletions vue-app/src/api/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,7 @@ if (!['simple', 'optimistic', 'kleros'].includes(recipientRegistryType as string
export const recipientRegistryPolicy = import.meta.env.VITE_RECIPIENT_REGISTRY_POLICY
export const operator: string = import.meta.env.VITE_OPERATOR || 'Clr.fund'

export const SUBGRAPH_ENDPOINT =
import.meta.env.VITE_SUBGRAPH_URL || 'https://api.thegraph.com/subgraphs/name/clrfund/clrfund'

export const isActiveApp = Boolean(import.meta.env.VITE_SUBGRAPH_URL)
export const SUBGRAPH_ENDPOINT = import.meta.env.VITE_SUBGRAPH_URL || ''

// application theme
export enum ThemeMode {
Expand Down
23 changes: 18 additions & 5 deletions vue-app/src/api/leaderboard.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
import leaderboardRounds from '@/rounds/rounds.json'
import { isSameAddress } from '@/utils/accounts'

type LeaderboardRecord = {
address: string
network: string
}

export async function getLeaderboardData(roundAddress: string, network: string) {
function isSameNetwork(network1 = '', network2 = ''): boolean {
return network1.toLowerCase() === network2.toLowerCase()
}

export async function getLeaderboardData(roundAddress: string, network?: string) {
const rounds = leaderboardRounds as LeaderboardRecord[]
const checkNetwork = Boolean(network)

const lowercaseRoundAddress = (roundAddress || '').toLowerCase()
const lowercaseNetwork = (network || '').toLowerCase()
const found = rounds.find((r: LeaderboardRecord) => {
return r.address.toLowerCase() === lowercaseRoundAddress && r.network.toLowerCase() === lowercaseNetwork
return isSameAddress(r.address, roundAddress) && (!checkNetwork || isSameNetwork(network, r.network))
})

return found ? import(`../rounds/${found.network}/${found.address}.json`) : null
if (!found) {
return null
}

const data = await import(`../rounds/${found.network}/${found.address}.json`)
if (!data.round) {
data.round = {}
}
data.round.network = found.network
return data
}
9 changes: 7 additions & 2 deletions vue-app/src/api/maci-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ export async function getMACIFactoryInfo(maxRecipients?: number): Promise<MACIFa

if (maxRecipients === undefined) {
const maciFactory = new Contract(maciFactoryAddress, MACIFactoryABI, provider)
const treeDepths = await maciFactory.treeDepths()
maxRecipients = 5 ** getNumber(treeDepths.voteOptionTreeDepth) - 1
try {
const treeDepths = await maciFactory.treeDepths()
maxRecipients = 5 ** getNumber(treeDepths.voteOptionTreeDepth) - 1
} catch {
// treeDepths doesn't exist on older version of maciFactory
maxRecipients = 124
}
}

return {
Expand Down
17 changes: 12 additions & 5 deletions vue-app/src/api/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import KlerosRegistry from './recipient-registry-kleros'
import sdk from '@/graphql/sdk'
import { getLeaderboardData } from '@/api/leaderboard'
import type { RecipientApplicationData } from '@/api/types'
import type { GetRecipientByIndexQuery } from '@/graphql/API'

export interface LeaderboardProject {
id: string // Address or another ID depending on registry implementation
Expand Down Expand Up @@ -117,10 +118,15 @@ export async function getProjectByIndex(
registryAddress: string,
recipientIndex: number,
): Promise<Partial<Project> | null> {
const result = await sdk.GetRecipientByIndex({
registryAddress: registryAddress.toLowerCase(),
recipientIndex,
})
let result: GetRecipientByIndexQuery
try {
result = await sdk.GetRecipientByIndex({
registryAddress: registryAddress.toLowerCase(),
recipientIndex,
})
} catch {
return null
}

if (!result.recipients.length) {
return null
Expand Down Expand Up @@ -186,7 +192,7 @@ export function toLeaderboardProject(project: any): LeaderboardProject {
return {
id: project.id,
name: project.name,
index: getNumber(project.recipientIndex),
index: getNumber(project.recipientIndex || 0),
imageUrl,
allocatedAmount: BigInt(project.allocatedAmount || '0'),
votes: BigInt(project.tallyResult || '0'),
Expand Down Expand Up @@ -284,6 +290,7 @@ export function staticDataToProjectInterface(project: any): Project {
websiteUrl: project.metadata.websiteUrl,
twitterUrl: project.metadata.twitterUrl,
discordUrl: project.discordUrl,
imageUrl: `${ipfsGatewayUrl}/ipfs/${project.metadata.imageHash}`,
bannerImageUrl: `${ipfsGatewayUrl}/ipfs/${project.metadata.bannerImageHash}`,
thumbnailImageUrl: `${ipfsGatewayUrl}/ipfs/${project.metadata.thumbnailImageHash}`,
index: project.recipientIndex,
Expand Down
Loading

0 comments on commit b17fed6

Please sign in to comment.