Skip to content

Commit

Permalink
feat(WIP): read proposals from v2
Browse files Browse the repository at this point in the history
  • Loading branch information
antomor committed Nov 13, 2024
1 parent 45c22e5 commit daae3cc
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 73 deletions.
23 changes: 15 additions & 8 deletions .env.testnet.local
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ NEXT_PUBLIC_BUILD_ID=

NEXT_PUBLIC_RIF_ADDRESS=0x19f64674d8a5b4e652319f5e239efd3bc969a1fe
NEXT_PUBLIC_STRIF_ADDRESS=0xC4b091d97AD25ceA5922f09fe80711B7ACBbb16f
NEXT_PUBLIC_GOVERNOR_ADDRESS=0x8F10473F1c2f1dA76F6E921aA6eE8c54aD43F7b7
NEXT_PUBLIC_EA_NFT_ADDRESS=0x979deF73ec80B8AE24Ae46765b81D9aF7b1C9327
NEXT_PUBLIC_GOVERNOR_ADDRESS=0xB1A39B8f57A55d1429324EEb1564122806eb297F
NEXT_PUBLIC_EA_NFT_ADDRESS=0x0Ee4e11f2F2B551cA31Ea7873c7bA675cb51A59d
NEXT_PUBLIC_MULTICALL_ADDRESS=0xcA11bde05977b3631167028862bE2a173976CA11
NEXT_PUBLIC_GRANTS_BUCKET_ADDRESS=0xfaca664c661af7e0e630c8f92b401012cd2a30ef
NEXT_PUBLIC_GRANTS_ACTIVE_BUCKET_ADDRESS=0x2217E4d3Ae0A6E30075D1B5a7b8C1520E8009f49
Expand All @@ -17,14 +17,21 @@ NEXT_PUBLIC_GENERAL_BUCKET_ADDRESS=0x72Ed7d7b7835Ad62B1f9b6280bAd62618aA71461
NEXT_PUBLIC_CHAIN_ID=31

# CR-related env variables
NEXT_PUBLIC_SIMPLIFIED_REWARD_DISTRIBUTOR_ADDRESS=0x4e84FCc953dE129C6C47c5B0AD7E57B226093Ae1
NEXT_PUBLIC_BACKERS_MANAGER_ADDRESS=0xC2857F402096BfF24E8E05C2E047F8461d1af927
NEXT_PUBLIC_REWARD_DISTRIBUTOR_ADDRESS=0x21534EaE65041b85cA309C3258E967f305917F8F
NEXT_PUBLIC_GOVERNANCE_MANAGER_ADDRESS=0xAEdD29bebb0dd8e29702DD1e32346365F96aA016
# TODO: To be removed
NEXT_PUBLIC_SIMPLIFIED_REWARD_DISTRIBUTOR_ADDRESS=0xc469Cc2579De5C16210e9063B4E628bF8C46bA02

NEXT_PUBLIC_CYCLE_DURATION_IN_DAYS=2
NEXT_PUBLIC_BACKERS_MANAGER_ADDRESS=0xec0a29Df5180A6B04496dfAf2D827e36F4a0A52F
NEXT_PUBLIC_REWARD_DISTRIBUTOR_ADDRESS=0xD476E4804551595687C1f6F0a9C22dd1Bbfa0319
NEXT_PUBLIC_GOVERNANCE_MANAGER_ADDRESS=0xb7C6918d6aE6df2e147FF464271a94EAfF027E5D
NEXT_PUBLIC_CYCLE_DURATION_IN_DAYS=7
NEXT_PUBLIC_FIRST_CYCLE_START_DATE_ISO="1970-01-01T00:00:00Z"

NEXT_PUBLIC_ENV_DATA_URL="https://raw.githubusercontent.com/RootstockCollective/dao-frontend/develop/data.testnet.local.json"
NEXT_PUBLIC_ENV_DATA_URL="https://raw.githubusercontent.com/RootstockCollective/dao-frontend/develop/data.testnet.qa.json"

# OG NFT Contracts
NEXT_PUBLIC_OG_FOUNDERS=0x7E6d9969CAC008bAe5f7b144df3c955515404538
NEXT_PUBLIC_OG_PARTNERS=0x285046a90fb322E6BaCa4F38Bb884e3C0904F7EB
NEXT_PUBLIC_OG_CONTRIBUTORS=0xDC03B8fb7E47E4651f5008bD718a804726424A75

NEXT_PUBLIC_ENABLE_CORS_BYPASS=true
NEXT_PUBLIC_ENABLE_FEATURE_V2_REWARDS=true
4 changes: 3 additions & 1 deletion src/app/collective-rewards/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { CreateBuilderProposalEventLog } from '@/app/proposals/hooks/useFetchLat
import { ProposalState } from '@/shared/types'
import { Address } from 'viem'

export const builderStatusOptions = ['Whitelisted', 'In progress'] as const
export const BuilderStatusActive = 'Active'
export const BuilderStatusInProgress = 'In progress'
export const builderStatusOptions = [BuilderStatusActive, BuilderStatusInProgress] as const

export type BuilderStatus = (typeof builderStatusOptions)[number]

Expand Down
150 changes: 100 additions & 50 deletions src/app/collective-rewards/user/hooks/useGetBuilders.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import { useGetIsWhitelistedBuilder, useGetWhitelistedBuilders } from '@/app/collective-rewards/user/hooks'
import { BuilderInfo } from '@/app/collective-rewards/types'
import {
BuilderInfo,
BuilderStatus,
BuilderStatusActive,
BuilderStatusInProgress,
} from '@/app/collective-rewards/types'
import { useFetchCreateBuilderProposals } from '@/app/proposals/hooks/useFetchLatestProposals'
import { BuilderRegistryAbi } from '@/lib/abis/v2/BuilderRegistryAbi'
import { AVERAGE_BLOCKTIME } from '@/lib/constants'
import { BackersManagerAddress } from '@/lib/contracts'
import { useMemo } from 'react'
import { Address, getAddress, isAddressEqual } from 'viem'
import { Address, getAddress } from 'viem'
import { useReadContracts } from 'wagmi'
import { useGetGaugesArray } from './useGetGaugesArray'

export type BuilderLoader = {
data?: BuilderInfo
Expand All @@ -14,67 +23,108 @@ export type BuildersLoader = Omit<BuilderLoader, 'data'> & {
data: BuilderInfo[]
}

export const useGetBuilderByAddress = (address: Address): BuilderLoader => {
const {
data: buildersProposalsMap,
isLoading: builderProposalsMapLoading,
error: builderProposalsMapError,
} = useFetchCreateBuilderProposals()
type BuilderStatusMap = Record<Address, BuilderStatus>

const EXCLUDED_BUILDER_STATUS = 'X'
export const useGetBuilders = (): BuildersLoader => {
/*
* get Gauges
* for each Gauge
* get Builder from Gauge
* get Builder state
* ignore the builder if paused or revoked (to be confirmed)
*/
// get the gauges
const { data: gauges, isLoading: gaugesLoading, error: gaugesError } = useGetGaugesArray()
// get the builders for each gauge
const gaugeToBuilderCalls = gauges?.map(
gauge =>
({
address: BackersManagerAddress,
abi: BuilderRegistryAbi,
functionName: 'gaugeToBuilder',
args: [gauge],
}) as const,
)
const {
data: isWhitelistedBuilder,
isLoading: isWhitelistedBuilderLoading,
error: isWhitelistedBuilderError,
} = useGetIsWhitelistedBuilder(address)
data: buildersResult,
isLoading: buildersLoading,
error: buildersError,
} = useReadContracts<Address[]>({
contracts: gaugeToBuilderCalls,
query: {
refetchInterval: AVERAGE_BLOCKTIME,
},
})
const builders = buildersResult?.map(builder => builder.result) as Address[]

const data = useMemo(() => {
if (buildersProposalsMap) {
const proposals = buildersProposalsMap?.[address] ?? {}
// get the builder state for each builder
const builderStatesCalls = builders?.map(
builder =>
({
address: BackersManagerAddress,
abi: BuilderRegistryAbi,
functionName: 'builderState',
args: [builder],
}) as const,
)
const {
data: builderStatesResult,
isLoading: builderStatesLoading,
error: builderStatesError,
} = useReadContracts({ contracts: builderStatesCalls, query: { refetchInterval: AVERAGE_BLOCKTIME } })
const builderStates = builderStatesResult?.map(
builderState =>
builderState.result as readonly [
boolean,
boolean,
boolean,
boolean,
boolean,
`0x${string}`,
`0x${string}`,
],
)

const builderStatusMap = builders
?.map((builder, index) => {
const [activated, kycApproved, whitelisted, paused, , ,] = builderStates?.[index] ?? []
return {
address,
status: isWhitelistedBuilder ? 'Whitelisted' : 'In progress',
proposals: Object.values(proposals),
} as BuilderInfo
}
}, [buildersProposalsMap, address, isWhitelistedBuilder])

const isLoading = builderProposalsMapLoading || isWhitelistedBuilderLoading
const error = builderProposalsMapError ?? isWhitelistedBuilderError
address: builder,
// TODO: to be refactored in a function
status: paused
? BuilderStatusActive
: activated && kycApproved && whitelisted
? BuilderStatusActive
: kycApproved || whitelisted
? BuilderStatusInProgress
: EXCLUDED_BUILDER_STATUS, // used to filter out builders
}
})
.filter(builder => builder.status !== EXCLUDED_BUILDER_STATUS)
.reduce<BuilderStatusMap>((acc, builder) => {
acc[builder.address] = builder.status as BuilderStatus
return acc
}, {})

return {
data,
isLoading,
error,
}
}

export const useGetBuilders = (): BuildersLoader => {
const {
data: buildersProposalsMap,
isLoading: builderProposalsMapLoading,
error: builderProposalsMapError,
} = useFetchCreateBuilderProposals()
const {
data: whitelistedBuilders,
isLoading: whitelistedBuildersLoading,
error: whitelistedBuildersError,
} = useGetWhitelistedBuilders()

const data = useMemo(() => {
return Object.entries(buildersProposalsMap ?? {}).map<BuilderInfo>(([builder, proposals]) => ({
address: getAddress(builder),
status: whitelistedBuilders?.some(whitelistedBuilder =>
isAddressEqual(whitelistedBuilder, getAddress(builder)),
)
? 'Whitelisted'
: 'In progress',
proposals: Object.values(proposals),
}))
}, [whitelistedBuilders, buildersProposalsMap])
return Object.entries(buildersProposalsMap ?? {})
.filter(([builder]) => builderStatusMap && builder in builderStatusMap)
.map<BuilderInfo>(([builder, proposals]) => ({
address: getAddress(builder),
status: builderStatusMap[builder as Address],
proposals: Object.values(proposals),
}))
}, [builderStatusMap, buildersProposalsMap])

const isLoading = builderProposalsMapLoading || whitelistedBuildersLoading
const error = builderProposalsMapError ?? whitelistedBuildersError
const isLoading = builderProposalsMapLoading || builderStatesLoading || buildersLoading
const error = builderProposalsMapError ?? builderStatesError ?? buildersError

return {
data,
Expand Down
53 changes: 53 additions & 0 deletions src/app/collective-rewards/user/hooks/useGetGaugesArray.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { AVERAGE_BLOCKTIME } from '@/lib/constants'
import { Address } from 'viem'
import { useReadContract, useReadContracts } from 'wagmi'
import { BuilderRegistryAbi } from '../../../../lib/abis/v2/BuilderRegistryAbi'
import { BackersManagerAddress } from '../../../../lib/contracts'

// TODO: to be rebased since already included in another PR
export const useGetGaugesArray = () => {
const {
data: gaugesLength,
isLoading: gaugesLengthLoading,
error: gaugesLengthError,
} = useReadContract({
address: BackersManagerAddress,
abi: BuilderRegistryAbi,
functionName: 'getGaugesLength',
query: {
refetchInterval: AVERAGE_BLOCKTIME,
},
})

const length = gaugesLength ? Number(gaugesLength) : 0

const contractCalls = Array.from({ length }, (_, index) => {
return {
address: BackersManagerAddress,
abi: BuilderRegistryAbi,
functionName: 'getGaugeAt',
args: [index],
} as const
})

const {
data: gaugesAddress,
isLoading: gaugesAddressLoading,
error: gaugesAddressError,
} = useReadContracts<Address[]>({
contracts: contractCalls,
query: {
refetchInterval: AVERAGE_BLOCKTIME,
},
})

const gauges = gaugesAddress?.map(gauge => gauge.result as Address)
const isLoading = gaugesLengthLoading || gaugesAddressLoading
const error = gaugesLengthError ?? gaugesAddressError

return {
data: gauges,
isLoading,
error,
}
}
15 changes: 12 additions & 3 deletions src/app/collective-rewards/utils/getMostAdvancedProposal.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import { ProposalState } from '@/shared/types'
import { BuilderInfo, ProposalsToState } from '@/app/collective-rewards/types'
import {
BuilderInfo,
BuilderStatus,
BuilderStatusActive,
BuilderStatusInProgress,
ProposalsToState,
} from '@/app/collective-rewards/types'

const inactiveProposalsStates = [ProposalState.Canceled, ProposalState.Defeated, ProposalState.Expired]
const isActive = (state: ProposalState) => !inactiveProposalsStates.includes(state)
const isBuilderInProgress = (builderStatus: BuilderStatus) =>
[BuilderStatusActive, BuilderStatusInProgress].includes(builderStatus)

export const getMostAdvancedProposal = (
{ status, proposals }: BuilderInfo,
Expand All @@ -12,9 +20,10 @@ export const getMostAdvancedProposal = (
.sort(({ timeStamp: a }, { timeStamp: b }) => b - a)
.find(({ args: { proposalId } }) => {
const state = proposalsStateMap[proposalId.toString()]

// TODO: To be refactored
// Get the proposal only if the Builder is Active or In Progress
const isExecuted = ProposalState.Executed === state
if (status === 'Whitelisted') {
if (isBuilderInProgress(status)) {
return isExecuted
}

Expand Down
24 changes: 16 additions & 8 deletions src/app/collective-rewards/whitelist/WhitelistSection.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useAlertContext } from '@/app/providers/AlertProvider'
import { LoadingSpinner } from '@/components/LoadingSpinner'
import { HeaderTitle } from '@/components/Typography'
import { useEffect } from 'react'
import React, { useEffect } from 'react'
import { WhitelistGrid, WhitelistSearch } from './components'
import { useWhitelistContext } from './context'
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/Collapsible'

export const WhitelistSection = () => {
const { builders, isLoading, error: whitelistError } = useWhitelistContext()
Expand All @@ -21,13 +22,20 @@ export const WhitelistSection = () => {
}, [whitelistError, setErrorMessage])

return (
<div>
<HeaderTitle>Activated Builders</HeaderTitle>
<WhitelistSearch />
<>
<Collapsible defaultOpen>
<CollapsibleTrigger>
<HeaderTitle>Activated Builders</HeaderTitle>
</CollapsibleTrigger>

{/* TODO: We should show an empty table (not considered in the design yet) on error */}
{isLoading && <LoadingSpinner />}
{!isLoading && <WhitelistGrid items={builders} />}
</div>
<CollapsibleContent>
<WhitelistSearch />

{/* TODO: We should show an empty table (not considered in the design yet) on error */}
{isLoading && <LoadingSpinner />}
{!isLoading && <WhitelistGrid items={builders} />}
</CollapsibleContent>
</Collapsible>
</>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { CreateBuilderProposalEventLog } from '@/app/proposals/hooks/useFetchLat
import { useMemo } from 'react'
import { ProposalState } from '@/shared/types'
import { ProposalsToState } from '@/app/collective-rewards/types'
import { AVERAGE_BLOCKTIME } from '@/lib/constants'

export const useGetProposalsState = (proposals: CreateBuilderProposalEventLog[]) => {
const contractCalls = proposals.map(({ args: { proposalId } }) => {
Expand All @@ -24,7 +25,7 @@ export const useGetProposalsState = (proposals: CreateBuilderProposalEventLog[])
} = useReadContracts<ProposalState[]>({
contracts: contractCalls,
query: {
refetchInterval: 30_000,
refetchInterval: AVERAGE_BLOCKTIME,
},
})

Expand Down
5 changes: 3 additions & 2 deletions src/app/proposals/hooks/useFetchLatestProposals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Interface } from 'ethers'
import { useMemo } from 'react'
import { getAddress, parseEventLogs } from 'viem'
import { ADDRESS_PADDING_LENGTH, RELAY_PARAMETER_PADDING_LENGTH } from '@/app/proposals/shared/utils'
import { BuilderRegistryAbi } from '@/lib/abis/v2/BuilderRegistryAbi'

const useFetchLatestProposals = () => {
return useQuery({
Expand Down Expand Up @@ -50,11 +51,11 @@ if (!RELAY_FUNCTION_SELECTOR) {
}

const CR_WHITELIST_FUNCTION = 'whitelistBuilder' // TODO: refactor
const CR_WHITELIST_FUNCTION_SELECTOR = new Interface(SimplifiedRewardDistributorAbi).getFunction(
const CR_WHITELIST_FUNCTION_SELECTOR = new Interface(BuilderRegistryAbi).getFunction(
CR_WHITELIST_FUNCTION,
)?.selector
if (!CR_WHITELIST_FUNCTION_SELECTOR) {
throw new Error(`Function ${CR_WHITELIST_FUNCTION} not found in SimplifiedRewardDistributorAbi.`)
throw new Error(`Function ${CR_WHITELIST_FUNCTION} not found in BuilderRegistryAbi.`)
}

type ElementType<T> = T extends (infer U)[] ? U : never
Expand Down

0 comments on commit daae3cc

Please sign in to comment.