diff --git a/src/modules/explorer/components/ArbitraryContractInteractionForm.tsx b/src/modules/explorer/components/ArbitraryContractInteractionForm.tsx
index 0842eea49..452fb679b 100644
--- a/src/modules/explorer/components/ArbitraryContractInteractionForm.tsx
+++ b/src/modules/explorer/components/ArbitraryContractInteractionForm.tsx
@@ -27,12 +27,12 @@ import ProposalExecuteForm from "./ProposalExecuteForm"
import { Expr, MichelsonData } from "@taquito/michel-codec"
import { getContract } from "services/contracts/baseDAO"
import { TezosToolkit } from "@taquito/taquito"
+import { BigNumber } from "bignumber.js"
import { useDAO } from "services/services/dao/hooks/useDAO"
import { useDAOID } from "../pages/DAO/router"
import AppConfig from "config"
import { Link } from "react-router-dom"
import { Button } from "components/ui/Button"
-import { ResponsiveDialog } from "./ResponsiveDialog"
import { useNotification } from "modules/common/hooks/useNotification"
// Base ACI Lambda
@@ -250,13 +250,22 @@ const ContractInteractionForm = ({
}
if (!isAciDeployerDeployed && !isLoading) {
+ // the DAO has no aci_executor handler yet: explain and point to the add-function proposal (one dialog, closable)
return (
-
- We need to deploy the ACI Deployer Contract
-
- Deploy ACI Deployer Contract
-
-
+
+
+ In order to use open-ended Contract Calls, the DAO contract must be amended.
+
+
+ If you have the minimum amount of tokens for the proposal fee, click Submit to create a proposal for adding
+ the Contract Call capability. Once that proposal is executed, this form becomes available.
+
+
+
+
+
+
+
)
}
@@ -474,11 +483,16 @@ const ContractInteractionForm = ({
})
console.log("Lambda Param", param)
+ // the form takes tez, the lambda pushes mutez
+ const amountMutez = new BigNumber(values.amount || 0)
+ .multipliedBy(1_000_000)
+ .integerValue(BigNumber.ROUND_DOWN)
+ .toNumber()
const lambda = generateExecuteContractMichelson("1.0.0", {
address: values.destination_contract_address,
entrypoint,
type,
- amount: values.amount,
+ amount: amountMutez,
param
})
@@ -534,7 +548,6 @@ export const ArbitraryContractInteractionForm: React.FC<{
showHeader: (state: boolean) => void
}> = ({ daoLambdas, showHeader }) => {
const daoId = useDAOID()
- const [aciProposalOpen, setAciProposalOpen] = useState(false)
const isInvalidKtOrTzAddress = (address: string) => validateContractAddress(address) !== 3
const initialValue: ACIValues = {
@@ -578,8 +591,6 @@ export const ArbitraryContractInteractionForm: React.FC<{
console.log({ daoLambdas })
}, [daoLambdas])
- const isAciDeployerDeployed = daoLambdas?.find((lambda: any) => lambda.key === AppConfig.ACI.EXECUTOR_FUNCTION_NAME)
-
return (
- {daoLambdas !== undefined && (
- setAciProposalOpen(false)}
- title={"Proposal to Enable Arbitrary Contract Interaction"}
- template="sm"
- >
- In order to use open-ended Contract Calls, the DAO contract must be amended.
-
- If you have the minimum amount of tokens for the proposal fee, click Submit to create a proposal for
- adding the ACI capability.
-
-
-
-
-
- )}
)
}}
diff --git a/src/modules/explorer/components/VoteDialog.tsx b/src/modules/explorer/components/VoteDialog.tsx
index 252e2584e..eef8b4bcb 100644
--- a/src/modules/explorer/components/VoteDialog.tsx
+++ b/src/modules/explorer/components/VoteDialog.tsx
@@ -1,6 +1,6 @@
import React, { useCallback, useEffect, useMemo } from "react"
import { Grid, Typography, TextField, Button, useTheme, styled } from "@mui/material"
-import { useParams } from "react-router-dom"
+import { Link, useParams } from "react-router-dom"
import { useVote } from "services/contracts/baseDAO/hooks/useVote"
import BigNumber from "bignumber.js"
import { useDAO } from "services/services/dao/hooks/useDAO"
@@ -13,6 +13,8 @@ import { ProposalFormInput } from "./ProposalFormInput"
import { getUserTokenBalance } from "services/bakingBad/tokenBalances"
import { useTezos } from "services/beacon/hooks/useTezos"
import { useTezosBalance } from "services/contracts/baseDAO/hooks/useTezosBalance"
+import { useProposals } from "services/services/dao/hooks/useProposals"
+import { ProposalStatus } from "services/services/dao/mappers/proposal/types"
const CustomLabelsContainer = styled(Grid)({
marginBottom: 12
@@ -31,6 +33,14 @@ const CustomMaxLabel = styled(Typography)({
marginLeft: 12
})
+const HintText = styled(Typography)(({ theme }) => ({
+ "fontSize": 14,
+ "marginTop": 12,
+ "color": theme.palette.text.primary,
+ "opacity": 0.75,
+ "& a": { color: "inherit", textDecoration: "underline" }
+}))
+
const ErrorText = styled(Typography)({
fontSize: 13,
marginTop: 8,
@@ -82,6 +92,17 @@ export const VoteDialog: React.FC<{
}, [amount, dao, mutate, onClose, proposalId, support, validate])
const { account } = useTezos()
+ // votes stay staked after a proposal is executed or dropped until the holder releases them
+ const { data: executedProposals } = useProposals(daoId, ProposalStatus.EXECUTED)
+ const { data: droppedProposals } = useProposals(daoId, ProposalStatus.DROPPED)
+ const releasableStake = useMemo(
+ () =>
+ (executedProposals ?? []).concat(droppedProposals ?? []).reduce((acc, finishedProposal) => {
+ const vote = finishedProposal.voters.find(voter => voter.address.toLowerCase() === account?.toLowerCase())
+ return vote && vote.staked ? acc.plus(vote.value) : acc
+ }, new BigNumber(0)),
+ [executedProposals, droppedProposals, account]
+ )
useMemo(() => {
const found = ledger?.find(l => l.holder.address.toLowerCase() === account.toLowerCase())
@@ -157,6 +178,12 @@ export const VoteDialog: React.FC<{
/>
{error ? Amount cannot be greater than max available : null}
+ {releasableStake.gt(0) ? (
+
+ {releasableStake.toFormat()} {dao?.data.token.symbol} are still staked on finished proposals. Release
+ them with Unstake on the User page to vote with them.
+
+ ) : null}
diff --git a/src/modules/explorer/pages/ProposalDetails/index.tsx b/src/modules/explorer/pages/ProposalDetails/index.tsx
index 70c70b507..c34e1eb4d 100644
--- a/src/modules/explorer/pages/ProposalDetails/index.tsx
+++ b/src/modules/explorer/pages/ProposalDetails/index.tsx
@@ -160,6 +160,19 @@ const formatConfig = {
trimMantissa: true
}
+const proposalKindLabel = (proposal: Proposal | undefined): string => {
+ if (!proposal || !(proposal instanceof LambdaProposal)) {
+ return "Treasury Proposal"
+ }
+ const { lambdaType, lambdaHandler } = proposal.metadata
+ if (lambdaType === "add_handler") return "Add Function Proposal"
+ if (lambdaType === "remove_handler") return "Remove Function Proposal"
+ if (lambdaType === "execute_handler") {
+ return lambdaHandler?.handler_name === "aci_executor" ? "Contract Call Proposal" : "Execute Function Proposal"
+ }
+ return "Proposal"
+}
+
export const ProposalDetails: React.FC = () => {
const { proposalId } = useParams<{
proposalId: string
@@ -333,7 +346,7 @@ export const ProposalDetails: React.FC = () => {
- Treasury Proposal • Created by
+ {proposalKindLabel(proposal)} • Created by
{proposal && cycleInfo && (
{
- const [isLoading, setIsLoading] = useState(true)
- const [proposalData, setProposalData] = useState([])
- const { data: daoData, isLoading: isLoadingDAO, error, cycleInfo } = useDAO(contractAddress)
+ const { data: daoData, error, cycleInfo } = useDAO(contractAddress)
const queryResults = useQuery({
- queryKey: ["proposals", contractAddress, status],
- queryFn: async () => {
- const dao = daoData as BaseDAO
- const proposals = await getProposals(dao)
-
- if (!status) {
- return proposals
- }
-
- return proposals.filter(
- proposalData => proposalData.getStatus((cycleInfo as CycleInfo).currentLevel).status === status
- )
- },
+ queryKey: ["proposals", contractAddress],
+ queryFn: async () => getProposals(daoData as BaseDAO),
refetchInterval: 30000,
- enabled: !!daoData && !!cycleInfo
+ enabled: !!daoData
})
- useEffect(() => {
- if (queryResults.data) {
- setProposalData(queryResults.data)
+ const data = useMemo(() => {
+ const proposals = queryResults.data ?? []
+ if (!status) {
+ return proposals
+ }
+ if (!cycleInfo) {
+ return []
}
- }, [queryResults.data])
+ return proposals.filter(proposal => proposal.getStatus((cycleInfo as CycleInfo).currentLevel).status === status)
+ }, [queryResults.data, status, cycleInfo])
return {
- data: proposalData,
- isLoading: isLoading || queryResults.isLoading,
+ data,
+ isLoading: queryResults.isLoading,
error: error || queryResults.error
}
}