Skip to content

Commit

Permalink
Add interaction with contracts
Browse files Browse the repository at this point in the history
Signed-off-by: Manank Patni <[email protected]>
  • Loading branch information
Man-Jain committed Jul 15, 2023
1 parent 9dd5085 commit db30033
Show file tree
Hide file tree
Showing 10 changed files with 297 additions and 30 deletions.
10 changes: 4 additions & 6 deletions src/modules/explorer/components/UserBalances.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ const BalanceToken = styled(Typography)({
fontWeight: 300
})

export const UserBalances: React.FC<{ daoId: string; setVoteWeight: any }> = ({ daoId, children, setVoteWeight }) => {
export const UserBalances: React.FC<{ daoId: string }> = ({ daoId, children }) => {
const { account } = useTezos()
const { data: dao, ledger } = useDAO(daoId)
const theme = useTheme()
Expand Down Expand Up @@ -104,17 +104,15 @@ export const UserBalances: React.FC<{ daoId: string; setVoteWeight: any }> = ({
userBalances.available.balance = "-"
userBalances.pending.balance = "-"
userBalances.staked.balance = "-"
setVoteWeight("-")

return userBalances
}

userBalances.available.balance = userLedger.available_balance.dp(10, 1).toString()
userBalances.pending.balance = userLedger.pending_balance.dp(10, 1).toString()
userBalances.staked.balance = userLedger.staked.dp(10, 1).toString()
setVoteWeight(userBalances.staked.balance)
return userBalances
}, [account, ledger, setVoteWeight])
}, [account, ledger])

const balancesList = Object.keys(balances).map(key => balances[key as keyof Balances])

Expand Down Expand Up @@ -148,10 +146,10 @@ export const UserBalances: React.FC<{ daoId: string; setVoteWeight: any }> = ({
)
}

export const UserBalancesBox: React.FC<{ daoId: string; setVoteWeight: any }> = ({ daoId, setVoteWeight }) => {
export const UserBalancesBox: React.FC<{ daoId: string }> = ({ daoId }) => {
return (
<BalancesBox item>
<UserBalances daoId={daoId} setVoteWeight={setVoteWeight} />
<UserBalances daoId={daoId} />
</BalancesBox>
)
}
49 changes: 41 additions & 8 deletions src/modules/explorer/pages/User/components/DelegationBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import { Grid, Theme, Typography, styled } from "@material-ui/core"
import { useDAO } from "services/services/dao/hooks/useDAO"
import { Edit } from "@material-ui/icons"
import { DelegationDialog } from "./DelegationModal"
import { useDelegationStatus } from "services/contracts/token/hooks/useDelegationStatus"
import { useTezos } from "services/beacon/hooks/useTezos"
import { useDelegationVoteWeight } from "services/contracts/token/hooks/useDelegationVoteWeight"
import BigNumber from "bignumber.js"
import { parseUnits } from "services/contracts/utils"

export enum DelegationsType {
ACCEPTING_DELEGATION = "ACCEPTING_DELEGATION",
Expand Down Expand Up @@ -44,20 +49,39 @@ export const matchTextToStatus = (value: DelegationsType | undefined) => {
}
}

export const Delegation: React.FC<{ voteWeight: any; daoId: string }> = ({ voteWeight, daoId }) => {
export const Delegation: React.FC<{ daoId: string }> = ({ daoId }) => {
const { data: dao } = useDAO(daoId)
const [delegationStatus, setDelegationStatus] = useState<DelegationsType | undefined>(
DelegationsType.NOT_ACCEPTING_DELEGATION
)
const { network, tezos, account, connect } = useTezos()

const { data: delegatedTo } = useDelegationStatus(dao?.data.token.contract)
const [delegationStatus, setDelegationStatus] = useState<DelegationsType>(DelegationsType.NOT_ACCEPTING_DELEGATION)
const [openModal, setOpenModal] = useState(false)
const { data: delegateVoteBalances } = useDelegationVoteWeight(dao?.data.token.contract)
const [voteWeight, setVoteWeight] = useState(new BigNumber(0))
console.log("voteWeight: ", voteWeight.toString())

const onCloseAction = () => {
setOpenModal(false)
}

useEffect(() => {
console.log("se actualizó", delegationStatus)
}, [delegationStatus])
if (delegatedTo === account) {
setDelegationStatus(DelegationsType.ACCEPTING_DELEGATION)
} else if (delegatedTo && delegatedTo !== account) {
setDelegationStatus(DelegationsType.DELEGATING)
} else {
setDelegationStatus(DelegationsType.NOT_ACCEPTING_DELEGATION)
}
}, [delegatedTo, account])

useEffect(() => {
let totalVoteWeight = new BigNumber(0)
delegateVoteBalances?.forEach(delegatedVote => {
const balance = new BigNumber(delegatedVote.balance)
totalVoteWeight = totalVoteWeight.plus(balance)
})
setVoteWeight(totalVoteWeight)
}, [delegateVoteBalances])

return (
<DelegationBox container direction="column">
Expand All @@ -71,7 +95,11 @@ export const Delegation: React.FC<{ voteWeight: any; daoId: string }> = ({ voteW
<Grid container style={{ gap: 12 }} direction="column">
<Typography color="textPrimary">Voting Weight</Typography>
<Balance color="secondary">
{voteWeight} {voteWeight !== "-" ? dao.data.token.symbol : ""}
{!voteWeight || voteWeight.eq(new BigNumber(0)) ? (
"-"
) : (
<>{`${parseUnits(voteWeight, dao.data.token.decimals).toString()} ${dao.data.token.symbol}`}</>
)}
</Balance>
</Grid>
)}
Expand All @@ -95,13 +123,18 @@ export const Delegation: React.FC<{ voteWeight: any; daoId: string }> = ({ voteW
</Typography>
</Grid>
</Grid>
<Subtitle variant="body1">{matchTextToStatus(delegationStatus)}</Subtitle>
<Subtitle variant="body1">
{matchTextToStatus(delegationStatus)}
{delegationStatus === DelegationsType.DELEGATING ? delegatedTo : null}
</Subtitle>
</Grid>
<DelegationDialog
open={openModal}
onClose={onCloseAction}
status={delegationStatus}
setDelegationStatus={setDelegationStatus}
delegationStatus={delegationStatus}
delegatedTo={delegatedTo}
/>
</DelegationBox>
)
Expand Down
49 changes: 37 additions & 12 deletions src/modules/explorer/pages/User/components/DelegationModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import React, { useEffect, useState } from "react"
import { DelegationsType, matchTextToStatus } from "./DelegationBanner"
import { ResponsiveDialog } from "modules/explorer/components/ResponsiveDialog"
import { SmallButton } from "modules/common/SmallButton"
import { useTokenDelegate } from "services/contracts/token/hooks/useTokenDelegate"
import { useDAO } from "services/services/dao/hooks/useDAO"
import { useDAOID } from "../../DAO/router"
import { useTezos } from "services/beacon/hooks/useTezos"

const AddressTextField = styled(TextField)({
"backgroundColor": "#2f3438",
Expand Down Expand Up @@ -50,10 +54,17 @@ export const DelegationDialog: React.FC<{
open: boolean
onClose: () => void
status: DelegationsType | undefined
setDelegationStatus: (value: DelegationsType | undefined) => void
}> = ({ status, onClose, open, setDelegationStatus }) => {
setDelegationStatus: (value: DelegationsType) => void
delegationStatus: DelegationsType
delegatedTo: string | null | undefined
}> = ({ status, onClose, open, setDelegationStatus, delegationStatus, delegatedTo }) => {
const [options, setOptions] = useState<ActionTypes[]>([])
const [selectedOption, setSelectedOption] = useState()
const { mutate: delegateToken } = useTokenDelegate()
const daoId = useDAOID()
const { data, cycleInfo } = useDAO(daoId)
const { tezos, connect, network, account } = useTezos()
const [newDelegate, setNewDelegate] = useState("")

useEffect(() => {
getOptionsByStatus(status)
Expand All @@ -71,13 +82,20 @@ export const DelegationDialog: React.FC<{

const updateStatus = () => {
if (selectedOption === ActionTypes.DELEGATE || selectedOption === ActionTypes.CHANGE_DELEGATE) {
return setDelegationStatus(DelegationsType.DELEGATING)
}
if (selectedOption === ActionTypes.STOP_ACCEPTING_DELEGATIONS || ActionTypes.STOP_DELEGATING) {
return setDelegationStatus(DelegationsType.NOT_ACCEPTING_DELEGATION)
}
if (selectedOption === ActionTypes.ACCEPT_DELEGATIONS) {
return setDelegationStatus(DelegationsType.ACCEPTING_DELEGATION)
if (newDelegate && data?.data.token.contract) {
delegateToken({ tokenAddress: data?.data.token.contract, delegateAddress: newDelegate })
}
} else if (
selectedOption === ActionTypes.STOP_ACCEPTING_DELEGATIONS ||
selectedOption === ActionTypes.STOP_DELEGATING
) {
if (data?.data.token.contract) {
delegateToken({ tokenAddress: data?.data.token.contract, delegateAddress: null })
}
} else if (selectedOption === ActionTypes.ACCEPT_DELEGATIONS) {
if (data?.data.token.contract && account) {
delegateToken({ tokenAddress: data?.data.token.contract, delegateAddress: account })
}
}
}

Expand All @@ -88,7 +106,7 @@ export const DelegationDialog: React.FC<{
setOptions(optionsOne)
break
case DelegationsType.ACCEPTING_DELEGATION:
const optionsTwo = [ActionTypes.STOP_ACCEPTING_DELEGATIONS, ActionTypes.DELEGATE]
const optionsTwo = [ActionTypes.STOP_ACCEPTING_DELEGATIONS]
setOptions(optionsTwo)
break
case DelegationsType.DELEGATING:
Expand All @@ -104,7 +122,7 @@ export const DelegationDialog: React.FC<{
<Grid item style={{ gap: 8 }} container direction="column">
<Typography color="textPrimary">Current Status</Typography>
<Typography color="secondary" style={{ fontWeight: 200 }}>
{matchTextToStatus(status)}
{matchTextToStatus(status)} {delegationStatus === DelegationsType.DELEGATING ? delegatedTo : null}
</Typography>
</Grid>

Expand All @@ -130,7 +148,14 @@ export const DelegationDialog: React.FC<{
/>
{item === selectedOption &&
(selectedOption === ActionTypes.DELEGATE || selectedOption === ActionTypes.CHANGE_DELEGATE) ? (
<AddressTextField type="text" placeholder="Enter Address" InputProps={{ disableUnderline: true }} />
<AddressTextField
onChange={e => {
setNewDelegate(e.target.value)
}}
type="text"
placeholder="Enter Address"
InputProps={{ disableUnderline: true }}
/>
) : null}
</Grid>
</>
Expand Down
5 changes: 2 additions & 3 deletions src/modules/explorer/pages/User/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ export const User: React.FC = () => {
const { mutate: unstakeFromAllProposals } = useUnstakeFromAllProposals()
const polls = usePolls(data?.liteDAOData?._id)
const pollsPosted = polls?.filter(p => p.author === account)
const [voteWeight, setVoteWeight] = useState()

useEffect(() => {
if (!account) {
Expand Down Expand Up @@ -168,7 +167,7 @@ export const User: React.FC = () => {
<MainContainer>
<Grid container direction="column" style={{ gap: 40 }} wrap={"nowrap"}>
<BalancesHeader item>
<UserBalances daoId={daoId} setVoteWeight={setVoteWeight}>
<UserBalances daoId={daoId}>
<Grid item>
<Grid container alignItems="center" justifyContent="space-between" style={{ gap: 20 }}>
<Grid item>
Expand Down Expand Up @@ -208,7 +207,7 @@ export const User: React.FC = () => {
</UserBalances>
</BalancesHeader>

<Delegation voteWeight={voteWeight} daoId={daoId} />
<Delegation daoId={daoId} />
<Grid item>
{proposalsCreated && cycleInfo && (
<ProposalsList
Expand Down
64 changes: 63 additions & 1 deletion src/services/bakingBad/delegations/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Network } from "services/beacon"
import { networkNameMap } from ".."
import { DelegationDTO } from "./types"
import { DelegationDTO, TokenDelegationDTO, UserDelegateBalance } from "./types"
import { getUserTokenBalance } from "../tokenBalances"

export const getLatestDelegation = async (daoAddress: string, network: Network) => {
const url = `https://api.${networkNameMap[network]}.tzkt.io/v1/operations/delegations?sender=${daoAddress}&status=applied`
Expand All @@ -17,3 +18,64 @@ export const getLatestDelegation = async (daoAddress: string, network: Network)

return resultingDelegations[0]
}

export const getTokenDelegation = async (tokenAddress: string, account: string, network: Network) => {
const url = `https://api.${networkNameMap[network]}.tzkt.io/v1/contracts/${tokenAddress}/bigmaps/delegates/keys?key.eq=${account}&active=true`
const response = await fetch(url)

if (!response.ok) {
throw new Error("Failed to fetch token delegations from TZKT API")
}

const resultingDelegations: TokenDelegationDTO[] = await response.json()

if (resultingDelegations.length === 0) {
return null
}

const delegatedTo = resultingDelegations[0].value

return delegatedTo
}

export const getTokenDelegationVoteWeight = async (tokenAddress: string, account: string, network: Network) => {
const selfBalance = await getUserTokenBalance(account, network, tokenAddress)

if (!selfBalance) {
throw new Error("Could not fetch delegate token balance from the TZKT API")
}

const url = `https://api.${networkNameMap[network]}.tzkt.io/v1/contracts/${tokenAddress}/bigmaps/delegates/keys?value.eq=${account}&active=true`
const response = await fetch(url)

if (!response.ok) {
throw new Error("Failed to fetch token delegations from TZKT API")
}

const resultingDelegations: TokenDelegationDTO[] = await response.json()

const delegateBalance: UserDelegateBalance = {
address: account,
balance: selfBalance
}

if (resultingDelegations.length === 0) {
return [delegateBalance]
}

const delegatedAddressBalances: UserDelegateBalance[] = []

await Promise.all(
resultingDelegations.map(async del => {
const balance = await getUserTokenBalance(del.key, network, tokenAddress)
if (balance) {
delegatedAddressBalances.push({
address: del.key,
balance: balance
})
}
})
)

return delegatedAddressBalances
}
16 changes: 16 additions & 0 deletions src/services/bakingBad/delegations/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,19 @@ export interface DelegationDTO {
}
status: string
}

export interface TokenDelegationDTO {
id: number
active: boolean
hash: string
key: string
value: string
firstLevel: number
lastLevel: number
updates: number
}

export interface UserDelegateBalance {
address: string
balance: string
}
27 changes: 27 additions & 0 deletions src/services/contracts/token/hooks/useDelegationStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useQuery } from "react-query"
import { getTokenDelegation } from "services/bakingBad/delegations"
import { getDAOBalances } from "services/bakingBad/tokenBalances"
import { useTezos } from "services/beacon/hooks/useTezos"

export const useDelegationStatus = (tokenAddress: string | undefined) => {
const { network, tezos, account, connect } = useTezos()

const { data, ...rest } = useQuery<string | null, Error>(
["tokenDelegations", tokenAddress],
async () => {
if (!tokenAddress) {
return null
} else {
return await getTokenDelegation(tokenAddress, account, network)
}
},
{
enabled: !!tokenAddress
}
)

return {
data,
...rest
}
}
25 changes: 25 additions & 0 deletions src/services/contracts/token/hooks/useDelegationVoteWeight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useQuery } from "react-query"
import { getTokenDelegationVoteWeight } from "services/bakingBad/delegations"
import { UserDelegateBalance } from "services/bakingBad/delegations/types"
import { useTezos } from "services/beacon/hooks/useTezos"

export const useDelegationVoteWeight = (tokenAddress: string | undefined) => {
const { network, account } = useTezos()

const { data, ...rest } = useQuery<UserDelegateBalance[] | undefined, Error>(
["delegationVoteWeight", tokenAddress],
async () => {
if (tokenAddress) {
return await getTokenDelegationVoteWeight(tokenAddress, account, network)
}
},
{
enabled: !!tokenAddress
}
)

return {
data,
...rest
}
}
Loading

0 comments on commit db30033

Please sign in to comment.