Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/modules/explorer/hooks/useCanDropProposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ export const useCanDropProposal = (daoId: string, proposalId: string) => {

const isGuardian = dao.data.guardian.toLowerCase() === account.toLowerCase()

const isNotExecutedOrDropped = true
const isNotExecutedOrDropped =
(cycleInfo && proposal.getStatus(cycleInfo.currentLevel).status === ProposalStatus.DROPPED) ||
(cycleInfo && proposal.getStatus(cycleInfo.currentLevel).status === ProposalStatus.EXECUTED)

// dao.data.proposalsToFlush.find(
// (id) => id.toLowerCase() === proposal.id.toLowerCase()
// );

return isNotExecutedOrDropped && (isProposer || hasExpired || isGuardian)
return !isNotExecutedOrDropped && (isProposer || hasExpired || isGuardian)
}, [account, cycleInfo, dao, proposal])
}
30 changes: 15 additions & 15 deletions src/modules/explorer/pages/ProposalDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,12 @@ export const ProposalDetails: React.FC = () => {
})
}, [dao, dropProposal, proposalId])

const onUnstakeVotes = useCallback(async () => {
await mutateUnstake({
dao: dao as BaseDAO,
proposalId
})
}, [dao, mutateUnstake, proposalId])
// const onUnstakeVotes = useCallback(async () => {
// await mutateUnstake({
// dao: dao as BaseDAO,
// proposalId
// })
// }, [dao, mutateUnstake, proposalId])

const proposalCycle = proposal ? proposal.period : "-"

Expand Down Expand Up @@ -160,13 +160,13 @@ export const ProposalDetails: React.FC = () => {

const canVote = cycleInfo && proposal?.getStatus(cycleInfo.currentLevel).status === ProposalStatus.ACTIVE

const canUnstakeVotes =
cycleInfo &&
proposal &&
account &&
(proposal.getStatus(cycleInfo.currentLevel).status === ProposalStatus.DROPPED ||
proposal.getStatus(cycleInfo.currentLevel).status === ProposalStatus.EXECUTED) &&
proposal.voters.some(({ address }) => address.toLowerCase() === account.toLowerCase())
// const canUnstakeVotes =
// cycleInfo &&
// proposal &&
// account &&
// (proposal.getStatus(cycleInfo.currentLevel).status === ProposalStatus.DROPPED ||
// proposal.getStatus(cycleInfo.currentLevel).status === ProposalStatus.EXECUTED) &&
// proposal.voters.some(({ address }) => address.toLowerCase() === account.toLowerCase())

const parseReadableConfigValue = (configKey: keyof Proposal["metadata"]["config"], value: BigNumber) => {
if (dao) {
Expand Down Expand Up @@ -204,7 +204,7 @@ export const ProposalDetails: React.FC = () => {
<InfoIcon color="secondary" />
</Tooltip>
</Grid>
<Grid>
{/* <Grid>
<Button variant="contained" color="secondary" disabled={!canUnstakeVotes} onClick={onUnstakeVotes}>
Unstake votes
</Button>
Expand All @@ -214,7 +214,7 @@ export const ProposalDetails: React.FC = () => {
>
<InfoIcon color="secondary" />
</Tooltip>
</Grid>
</Grid> */}
</Grid>
<Grid item>
<Grid container justifyContent="space-between" alignItems="center">
Expand Down
50 changes: 47 additions & 3 deletions src/modules/explorer/pages/User/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Box, Grid, Theme, Typography } from "@material-ui/core"
import { styled } from "@material-ui/styles"
import dayjs from "dayjs"
import React, { useEffect, useMemo } from "react"
import React, { useCallback, useEffect, useMemo } from "react"
import { useHistory } from "react-router"
import { useAgoraTopic } from "services/agora/hooks/useTopic"
import { useTezos } from "services/beacon/hooks/useTezos"
Expand All @@ -16,6 +16,8 @@ import { FreezeDialog } from "../../components/FreezeDialog"
import { UserBalances } from "../../components/UserBalances"
import { StatusBadge } from "../../components/StatusBadge"
import { ProposalsList } from "../../components/ProposalsList"
import { useUnstakeFromAllProposals } from "services/indexer/dao/hooks/useUnstakeFromAllProposals"
import { DropButton } from "../Proposals"

const ContentBlockItem = styled(Grid)({
padding: "35px 52px",
Expand All @@ -36,7 +38,7 @@ const MainContainer = styled(Box)({
})

const UsernameText = styled(Typography)({
fontSize: 28,
fontSize: 18,
wordBreak: "break-all"
})

Expand Down Expand Up @@ -94,10 +96,14 @@ export const ProposalItem: React.FC<{
export const User: React.FC = () => {
const { account } = useTezos()
const daoId = useDAOID()
const { cycleInfo } = useDAO(daoId)
const { data, cycleInfo } = useDAO(daoId)
const { data: proposals } = useProposals(daoId)
const history = useHistory()

const { data: executedProposals } = useProposals(daoId, ProposalStatus.EXECUTED)
const { data: droppedProposals } = useProposals(daoId, ProposalStatus.DROPPED)
const { mutate: unstakeFromAllProposals } = useUnstakeFromAllProposals()

useEffect(() => {
if (!account) {
history.push(`../${daoId}`)
Expand All @@ -120,6 +126,34 @@ export const User: React.FC = () => {
return proposals.filter(p => p.voters.map(voter => voter.address.toLowerCase()).includes(account.toLowerCase()))
}, [account, proposals])

const onUnstakeFromAllProposals = useCallback(async () => {
if (droppedProposals && executedProposals && data) {
const allProposals = droppedProposals.concat(executedProposals)

const proposalsWithStakedTokens: Proposal[] = []

allProposals.forEach((proposal: Proposal) => {
const userVote = proposal.voters.find(voter => voter.address === account)
if (userVote && userVote.staked) {
proposalsWithStakedTokens.push(proposal)
}
})

unstakeFromAllProposals({
dao: data,
allProposals: proposalsWithStakedTokens.map(p => p.id)
})
return
}
}, [data, account, unstakeFromAllProposals, droppedProposals, executedProposals])

const canUnstakeVotes: boolean | undefined =
executedProposals &&
droppedProposals &&
executedProposals
.concat(droppedProposals)
.some(proposal => proposal.voters.find(vote => vote.address === account)?.staked)

const getVoteDecision = (proposal: Proposal) =>
proposal.voters.find(voter => voter.address.toLowerCase())?.support as boolean

Expand Down Expand Up @@ -150,6 +184,16 @@ export const User: React.FC = () => {
<Grid item>
<FreezeDialog freeze={false} />
</Grid>
<Grid item>
<DropButton
variant="contained"
color="secondary"
onClick={onUnstakeFromAllProposals}
disabled={!canUnstakeVotes}
>
Unstake Votes
</DropButton>
</Grid>
</Grid>
</Grid>
</Grid>
Expand Down
6 changes: 3 additions & 3 deletions src/services/config/constants.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
export enum EnvKey {
REACT_APP_ENV = "REACT_APP_ENV",
REACT_APP_NETWORK = "REACT_APP_NETWORK",
REACT_APP_HASURA_URL = "REACT_APP_HASURA_URL",
REACT_APP_HASURA_ADMIN_SECRET = "REACT_APP_HASURA_ADMIN_SECRET",
REACT_APP_CORS_PROXY_URL = "REACT_APP_CORS_PROXY_URL",
REACT_APP_MIXPANEL_TOKEN = "REACT_APP_MIXPANEL_TOKEN",
REACT_APP_MIXPANEL_DEBUG_ENABLED = "REACT_APP_MIXPANEL_DEBUG_ENABLED",
REACT_APP_LAUNCH_DARKLY_SDK_DEV = "REACT_APP_LAUNCH_DARKLY_SDK_DEV",
REACT_APP_LAUNCH_DARKLY_SDK_PROD = "REACT_APP_LAUNCH_DARKLY_SDK_PROD",
REACT_APP_URL = "REACT_APP_URL"
REACT_APP_URL = "REACT_APP_URL",
REACT_APP_HASURA_URL_V2 = "REACT_APP_HASURA_URL_V2",
REACT_APP_HASURA_ADMIN_SECRET_V2 = "REACT_APP_HASURA_ADMIN_SECRET_V2"
}

export enum FeatureFlag {
Expand Down
11 changes: 11 additions & 0 deletions src/services/contracts/baseDAO/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,17 @@ export abstract class BaseDAO {
return await batch.send()
}

public unstakeFromAllProposals = async (proposals: string[], account: string, tezos: TezosToolkit) => {
const daoContract = await getContract(tezos, this.data.address)
const initialBatch = await tezos.wallet.batch()

const batch = proposals.reduce((prev, current) => {
return prev.withContractCall(daoContract.methods.unstake_vote([current]))
}, initialBatch)

return await batch.send()
}

public sendXtz = async (xtzAmount: BigNumber, tezos: TezosToolkit) => {
const contract = await getContract(tezos, this.data.address)

Expand Down
54 changes: 54 additions & 0 deletions src/services/indexer/dao/hooks/useUnstakeFromAllProposals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { useNotification } from "modules/common/hooks/useNotification"
import { useMutation, useQueryClient } from "react-query"
import { useTezos } from "services/beacon/hooks/useTezos"
import { BaseDAO } from "services/contracts/baseDAO"
import { networkNameMap } from "../../../bakingBad"

export const useUnstakeFromAllProposals = () => {
const queryClient = useQueryClient()
const openNotification = useNotification()
const { network, tezos, account, connect } = useTezos()

return useMutation<any | Error, Error, { dao: BaseDAO; allProposals: string[] }>(
async params => {
const { key: unstakeNotification, closeSnackbar: closeFlushNotification } = openNotification({
message: "Please sign the transaction to unstake tokens from all proposals",
persist: true,
variant: "info"
})
try {
let tezosToolkit = tezos

if (!account) {
tezosToolkit = await connect()
}

const data = await params.dao.unstakeFromAllProposals(params.allProposals, account, tezosToolkit)
closeFlushNotification(unstakeNotification)

await data.confirmation(1)
openNotification({
message: "Execute transaction confirmed!",
autoHideDuration: 5000,
variant: "success",
detailsLink: `https://${networkNameMap[network]}.tzkt.io/` + data.opHash
})

return data
} catch (e) {
closeFlushNotification(unstakeNotification)
openNotification({
message: "An error has happened with execute transaction!",
variant: "error",
autoHideDuration: 5000
})
return new Error((e as Error).message)
}
},
{
onSuccess: () => {
queryClient.resetQueries()
}
}
)
}
4 changes: 3 additions & 1 deletion src/services/indexer/dao/mappers/proposal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export abstract class Proposal {
address: string
value: BigNumber
support: boolean
staked: boolean
}[]
type: DAOTemplate

Expand All @@ -87,7 +88,8 @@ export abstract class Proposal {
this.voters = dto.votes.map(vote => ({
address: vote.holder.address,
value: parseUnits(new BigNumber(vote.amount), this.dao.data.token.decimals),
support: Boolean(vote.support)
support: Boolean(vote.support),
staked: vote.staked
}))
this.upVotes = this.voters.reduce((acc, voter) => {
if (voter.support) {
Expand Down
2 changes: 2 additions & 0 deletions src/services/indexer/dao/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ export const GET_PROPOSALS_QUERY = gql`
}
id
support
staked
}
}
}
Expand Down Expand Up @@ -206,6 +207,7 @@ export const GET_PROPOSAL_QUERY = gql`
}
id
support
staked
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/services/indexer/graphql.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { GraphQLClient } from "graphql-request"
import { EnvKey, getEnv } from "services/config"

const BASE_URL = getEnv(EnvKey.REACT_APP_HASURA_URL)
const HASURA_ADMIN_SECRET = getEnv(EnvKey.REACT_APP_HASURA_ADMIN_SECRET)
const BASE_URL = getEnv(EnvKey.REACT_APP_HASURA_URL_V2)
const HASURA_ADMIN_SECRET = getEnv(EnvKey.REACT_APP_HASURA_ADMIN_SECRET_V2)

if (!BASE_URL) {
throw new Error(`${EnvKey.REACT_APP_HASURA_URL} env variable is missing`)
throw new Error(`${EnvKey.REACT_APP_HASURA_URL_V2} env variable is missing`)
}

if (!HASURA_ADMIN_SECRET) {
throw new Error(`${EnvKey.REACT_APP_HASURA_ADMIN_SECRET} env variable is missing`)
throw new Error(`${EnvKey.REACT_APP_HASURA_ADMIN_SECRET_V2} env variable is missing`)
}

export const client = new GraphQLClient(BASE_URL, {
Expand Down
1 change: 1 addition & 0 deletions src/services/indexer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ export interface VoteDTO {
amount: string
support: boolean
holder: HolderDTO
staked: boolean
}

export interface DAOListItem {
Expand Down